# Développement PHP (back)
SmartBoot ajoute à votre module Dolibarr toute la pile technique pour exposer une API REST.
## Structure des fichiers
Après SmartBoot, votre module contient :
```
monmodule/
├── smartmaker-api-prepend.php # Fichier d'initialisation
├── pwa/
│ ├── api.php # Routeur API
│ └── .htaccess # Redirection Apache
└── smartmaker-api/
└── Controllers/ # Vos contrôleurs
```
## smartmaker-api-prepend.php
Ce fichier initialise l'environnement SmartMaker :
* Entêtes Dolibarr obligatoires
* Chargement de l'autoloader SmartAuth
* Initialisation de la couche JWT
* Autoloader des classes de votre module
Ne modifiez pas ce fichier sauf si vous savez ce que vous faites.
## pwa/api.php - Le routeur
### Syntaxe des routes
```
Route::action(path, Controller::class, method, protected);
```
^ Paramètre ^ Description ^
| ''action'' | ''get'', ''post'', ''put'', ''delete'' |
| ''path'' | Chemin de l'API (ex: ''items'', ''items/{id}'') |
| ''Controller::class'' | Classe PHP à appeler |
| ''method'' | Méthode de la classe |
| ''protected'' | ''true'' = authentification requise |
### Exemple complet
```
= 2.2.16) :
```
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ api.php [QSA,L]
```
Pour nginx, une configuration équivalente est nécessaire dans le fichier de configuration du serveur.
## Créer un Controller
### Structure de base
```
entity;
$sql .= " ORDER BY date_creation DESC";
$resql = $db->query($sql);
while ($obj = $db->fetch_object($resql)) {
$item = new \MonObject($db);
$item->fetch($obj->rowid);
$mapping = new dmMonObject();
$items[] = $mapping->exportMappedData($item);
}
return [$items, 200];
}
/**
* Détail d'un item
* GET /items/{id}
*/
public function show($payload = null)
{
global $db;
$id = $payload['id'] ?? null;
if (!$id) {
return ['ID required', 400];
}
$item = new \MonObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not found', 404];
}
$item->fetch_optionals();
$item->fetch_lines();
$mapping = new dmMonObject();
$data = $mapping->exportMappedData($item);
return [$data, 200];
}
/**
* Créer un item
* POST /items
*/
public function create($payload = null)
{
global $db, $user;
$item = new \MonObject($db);
$item->label = $payload['label'] ?? '';
$item->description = $payload['description'] ?? '';
$item->fk_user = $user->id;
$res = $item->create($user);
if ($res < 0) {
return [$item->error ?: 'Creation failed', 500];
}
return [['id' => $res], 201];
}
/**
* Modifier un item
* PUT /items/{id}
*/
public function update($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
$item = new \MonObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not found', 404];
}
// Mettre à jour les champs fournis
if (isset($payload['label'])) {
$item->label = $payload['label'];
}
if (isset($payload['description'])) {
$item->description = $payload['description'];
}
$res = $item->update($user);
if ($res < 0) {
return [$item->error ?: 'Update failed', 500];
}
return ['Updated', 200];
}
/**
* Supprimer un item
* DELETE /items/{id}
*/
public function delete($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
$item = new \MonObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not found', 404];
}
$res = $item->delete($user);
if ($res < 0) {
return [$item->error ?: 'Delete failed', 500];
}
return ['Deleted', 200];
}
/**
* Recherche avec filtres
* POST /items/search/{filter}
*/
public function search($payload = null)
{
global $db, $user;
$filter = $payload['filter'] ?? 'all';
$limit = $payload['limit'] ?? 20;
$offset = $payload['offset'] ?? 0;
$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "monobject";
$sql .= " WHERE entity = " . $user->entity;
// Appliquer les filtres
switch ($filter) {
case 'active':
$sql .= " AND status = 1";
break;
case 'draft':
$sql .= " AND status = 0";
break;
case 'mine':
$sql .= " AND fk_user = " . $user->id;
break;
}
// Recherche texte
if (!empty($payload['search'])) {
$search = $db->escape($payload['search']);
$sql .= " AND (label LIKE '%$search%' OR description LIKE '%$search%')";
}
$sql .= " ORDER BY date_creation DESC";
$sql .= " LIMIT " . (int)$limit . " OFFSET " . (int)$offset;
$resql = $db->query($sql);
$items = [];
while ($obj = $db->fetch_object($resql)) {
$item = new \MonObject($db);
$item->fetch($obj->rowid);
$mapping = new dmMonObject();
$items[] = $mapping->exportMappedData($item);
}
return [$items, 200];
}
}
```
### Accès à l'utilisateur connecté
L'utilisateur JWT est disponible via ''$payload['user']'' ou la variable globale ''$user'' :
```
public function create($payload = null)
{
global $user;
// $user est l'objet User Dolibarr complet
dol_syslog("Action par: " . $user->login);
// Récupérer l'ID
$userId = $user->id;
// Vérifier les droits
if (!$user->rights->monmodule->write) {
return ['Permission denied', 403];
}
}
```
## Créer une classe de mapping (dm*)
Les classes ''dm*'' transforment les objets Dolibarr en JSON pour React :
```
champ JSON
protected $listOfPublishedFields = [
'rowid' => 'id',
'ref' => 'ref',
'label' => 'label',
'description' => 'description',
'fk_soc' => 'thirdparty', // Résolu automatiquement
'fk_statut' => 'status',
'date_creation' => 'created_at',
// Extrafields
'options_myfield' => 'my_field',
];
public function __construct()
{
global $langs;
$langs->load("monmodule@monmodule");
$this->boot();
}
/**
* Transformer une valeur avant l'envoi
* Méthode magique: fieldFilterValue + NomDuChamp
*/
public function fieldFilterValueCreatedAt($object)
{
return dol_print_date($object->date_creation, 'dayhour');
}
}
```
## Voir aussi
* [[../back/start|Back (PHP)]] - Documentation complète des routes
* [[../back/mapping_dolibarr_-_react|Mapping Dolibarr]] - Classes dm* en détail
* [[../smartauth/start|SmartAuth]] - Authentification JWT
* [[devfront|Développement React]] - Front-end