# 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