Chapitre 1 : Backend
1. Classe Dolibarr (optionnel)
Si vous utilisez un objet Dolibarr existant (Product, Facture, etc.), passez cette étape.
Pour un nouvel objet :
<?php
// class/task.class.php
require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
class Task extends CommonObject
{
public $element = 'task';
public $table_element = 'monmodule_task';
public $fk_element = 'fk_task';
public $id;
public $ref;
public $label;
public $description;
public $fk_user;
public $status;
public $priority;
public $date_start;
public $date_end;
public $date_creation;
public $tms;
const STATUS_DRAFT = 0;
const STATUS_TODO = 1;
const STATUS_DONE = 2;
public function __construct($db)
{
global $langs;
$this->db = $db;
}
public function create($user, $notrigger = 0)
{
$this->ref = $this->getNextNumRef();
$this->date_creation = dol_now();
$this->fk_user = $user->id;
$sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element;
$sql .= " (ref, label, description, fk_user, status, priority,";
$sql .= " date_start, date_end, date_creation, entity)";
$sql .= " VALUES (";
$sql .= "'" . $this->db->escape($this->ref) . "',";
$sql .= "'" . $this->db->escape($this->label) . "',";
$sql .= "'" . $this->db->escape($this->description) . "',";
$sql .= (int) $this->fk_user . ",";
$sql .= (int) $this->status . ",";
$sql .= (int) $this->priority . ",";
$sql .= ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : "NULL") . ",";
$sql .= ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : "NULL") . ",";
$sql .= "'" . $this->db->idate($this->date_creation) . "',";
$sql .= $conf->entity;
$sql .= ")";
$resql = $this->db->query($sql);
if ($resql) {
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
return $this->id;
}
$this->error = $this->db->lasterror();
return -1;
}
public function fetch($id, $ref = '')
{
$sql = "SELECT * FROM " . MAIN_DB_PREFIX . $this->table_element;
$sql .= " WHERE entity IN (" . getEntity($this->element) . ")";
if ($id) {
$sql .= " AND rowid = " . (int) $id;
} elseif ($ref) {
$sql .= " AND ref = '" . $this->db->escape($ref) . "'";
}
$resql = $this->db->query($sql);
if ($resql && $this->db->num_rows($resql)) {
$obj = $this->db->fetch_object($resql);
$this->id = $obj->rowid;
$this->ref = $obj->ref;
$this->label = $obj->label;
$this->description = $obj->description;
$this->fk_user = $obj->fk_user;
$this->status = $obj->status;
$this->priority = $obj->priority;
$this->date_start = $this->db->jdate($obj->date_start);
$this->date_end = $this->db->jdate($obj->date_end);
$this->date_creation = $this->db->jdate($obj->date_creation);
$this->tms = $this->db->jdate($obj->tms);
return 1;
}
return 0;
}
public function update($user, $notrigger = 0)
{
$sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
$sql .= " label = '" . $this->db->escape($this->label) . "',";
$sql .= " description = '" . $this->db->escape($this->description) . "',";
$sql .= " status = " . (int) $this->status . ",";
$sql .= " priority = " . (int) $this->priority . ",";
$sql .= " date_start = " . ($this->date_start ? "'" . $this->db->idate($this->date_start) . "'" : "NULL") . ",";
$sql .= " date_end = " . ($this->date_end ? "'" . $this->db->idate($this->date_end) . "'" : "NULL");
$sql .= " WHERE rowid = " . (int) $this->id;
$resql = $this->db->query($sql);
if ($resql) {
return 1;
}
$this->error = $this->db->lasterror();
return -1;
}
public function delete($user, $notrigger = 0)
{
$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element;
$sql .= " WHERE rowid = " . (int) $this->id;
$resql = $this->db->query($sql);
if ($resql) {
return 1;
}
$this->error = $this->db->lasterror();
return -1;
}
}
2. Routes API
<?php
// pwa/api.php
require_once '../smartmaker-api-prepend.php';
use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MonModule\Api\TaskController;
// Authentification
Route::get('login', AuthController::class, 'index');
Route::post('login', AuthController::class, 'login');
Route::get('refresh', AuthController::class, 'refresh');
Route::post('logout', AuthController::class, 'logout', true);
// Tâches
Route::get('tasks', TaskController::class, 'index', true);
Route::get('tasks/{id}', TaskController::class, 'show', true);
Route::post('tasks', TaskController::class, 'create', true);
Route::put('tasks/{id}', TaskController::class, 'update', true);
Route::delete('tasks/{id}', TaskController::class, 'delete', true);
// Fallback
json_reply('Access denied', 403);
3. Controller
<?php
// smartmaker-api/Controllers/TaskController.php
namespace MonModule\Api;
use MonModule\Api\Mappers\dmTask;
class TaskController
{
public function index($payload = null)
{
global $db, $user;
require_once DOL_DOCUMENT_ROOT . '/custom/monmodule/class/task.class.php';
// Filtres
$status = $payload['status'] ?? null;
$limit = min($payload['limit'] ?? 50, 100);
$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "monmodule_task";
$sql .= " WHERE fk_user = " . (int) $user->id;
$sql .= " AND entity IN (" . getEntity('task') . ")";
if ($status !== null) {
$sql .= " AND status = " . (int) $status;
}
$sql .= " ORDER BY priority DESC, date_start ASC";
$sql .= " LIMIT " . (int) $limit;
$resql = $db->query($sql);
$tasks = [];
$mapper = new dmTask();
while ($obj = $db->fetch_object($resql)) {
$task = new \Task($db);
$task->fetch($obj->rowid);
$tasks[] = $mapper->exportMappedData($task);
}
return [['tasks' => $tasks], 200];
}
public function show($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
require_once DOL_DOCUMENT_ROOT . '/custom/monmodule/class/task.class.php';
$task = new \Task($db);
$res = $task->fetch($id);
if ($res <= 0) {
return ['Task not found', 404];
}
// Vérifier que c'est bien la tâche de l'utilisateur
if ($task->fk_user != $user->id) {
return ['Forbidden', 403];
}
$mapper = new dmTask();
return [$mapper->exportMappedData($task), 200];
}
public function create($payload = null)
{
global $db, $user;
if (empty($payload['label'])) {
return ['Label required', 400];
}
require_once DOL_DOCUMENT_ROOT . '/custom/monmodule/class/task.class.php';
$task = new \Task($db);
$task->label = $payload['label'];
$task->description = $payload['description'] ?? '';
$task->status = $payload['status'] ?? 1;
$task->priority = $payload['priority'] ?? 0;
$task->date_start = $payload['date_start'] ?? null;
$task->date_end = $payload['date_end'] ?? null;
$res = $task->create($user);
if ($res < 0) {
return ['Error: ' . $task->error, 500];
}
return [['id' => $res], 201];
}
public function update($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
require_once DOL_DOCUMENT_ROOT . '/custom/monmodule/class/task.class.php';
$task = new \Task($db);
$res = $task->fetch($id);
if ($res <= 0) {
return ['Task not found', 404];
}
if ($task->fk_user != $user->id) {
return ['Forbidden', 403];
}
// Mise à jour des champs
if (isset($payload['label'])) $task->label = $payload['label'];
if (isset($payload['description'])) $task->description = $payload['description'];
if (isset($payload['status'])) $task->status = $payload['status'];
if (isset($payload['priority'])) $task->priority = $payload['priority'];
if (isset($payload['date_start'])) $task->date_start = $payload['date_start'];
if (isset($payload['date_end'])) $task->date_end = $payload['date_end'];
$res = $task->update($user);
if ($res < 0) {
return ['Error: ' . $task->error, 500];
}
return ['Updated', 200];
}
public function delete($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
require_once DOL_DOCUMENT_ROOT . '/custom/monmodule/class/task.class.php';
$task = new \Task($db);
$res = $task->fetch($id);
if ($res <= 0) {
return ['Task not found', 404];
}
if ($task->fk_user != $user->id) {
return ['Forbidden', 403];
}
$res = $task->delete($user);
if ($res < 0) {
return ['Error: ' . $task->error, 500];
}
return ['Deleted', 200];
}
}
4. Mapper
<?php
// smartmaker-api/Mappers/dmTask.php
namespace MonModule\Api\Mappers;
class dmTask extends \SmartAuth\DolibarrMapping\dmBase
{
use \SmartAuth\DolibarrMapping\dmTrait;
protected $type = "object";
protected $parentClassName = 'Task';
protected $listOfPublishedFields = [
'rowid' => 'id',
'ref' => 'ref',
'label' => 'label',
'description' => 'description',
'status' => 'status',
'priority' => 'priority',
'date_start' => 'date_start',
'date_end' => 'date_end',
'date_creation' => 'created_at'
];
public function __construct()
{
$this->boot();
}
public function fieldFilterValueStatus($object)
{
$labels = [
0 => 'draft',
1 => 'todo',
2 => 'done'
];
return $labels[$object->status] ?? 'unknown';
}
public function fieldFilterValueDateStart($object)
{
return $object->date_start ? date('Y-m-d', $object->date_start) : null;
}
public function fieldFilterValueDateEnd($object)
{
return $object->date_end ? date('Y-m-d', $object->date_end) : null;
}
}
Test de l'API
Avec curl ou Postman :
# Login
curl -X POST https://monsite.com/modules/monmodule/pwa/api.php \
-H "Content-Type: application/json" \
-d '{"login":"user@example.com","password":"secret"}'
# Liste des tâches
curl https://monsite.com/modules/monmodule/pwa/api.php?route=tasks \
-H "Authorization: Bearer <token>"
# Créer une tâche
curl -X POST https://monsite.com/modules/monmodule/pwa/api.php?route=tasks \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"label":"Ma tâche","priority":1}'