SmartMaker - Back (PHP)
The back-office part of SmartMaker fits into a standard Dolibarr module.
Folder structure
When you deploy SmartMaker in your Dolibarr module, the following folders are created:
| Folder | Description |
|---|---|
mobile/ |
React source code of the mobile application |
pwa/ |
Compiled application + api.php entry point |
smartmaker-api/ |
PHP controllers and mappers |
Plus a smartmaker-api-prepend.php file that factors out the includes.
The PHP router
Syntax
Route::action(path, Controller::class, method, protected);
| Parameter | Description |
|---|---|
action |
get, post, put, delete |
path |
API path (e.g. items, items/{id}) |
Controller::class |
PHP class to call |
method |
Method of the class |
protected |
true = authenticated route, false = public |
Complete example (api.php)
<?php
require_once '../smartmaker-api-prepend.php';
use SmartAuth\Api\AuthController;
use SmartAuth\Api\RouteController as Route;
use MyModule\Api\ItemController;
// Public routes (authentication)
Route::get('login', AuthController::class, 'index');
Route::post('login', AuthController::class, 'login');
Route::get('refresh', AuthController::class, 'refresh');
// Protected routes (require a JWT token)
Route::post('logout', AuthController::class, 'logout', true);
Route::post('device', AuthController::class, 'device', true);
// CRUD on the items
Route::get('items', ItemController::class, 'index', true);
Route::get('items/{id}', ItemController::class, 'show', true);
Route::post('items', ItemController::class, 'create', true);
Route::put('items/{id}', ItemController::class, 'update', true);
Route::delete('items/{id}', ItemController::class, 'delete', true);
// Routes with filters
Route::post('items/{filter}', ItemController::class, 'search', true);
// Files
Route::get('file/{element}/{parentid}/{ref}', FileController::class, 'download', true);
// Fallback
json_reply('Access denied', 403);
Dynamic parameters
Parameters between braces are passed in the payload:
// Route: GET /items/123
Route::get('items/{id}', ItemController::class, 'show', true);
// In the controller:
public function show($payload)
{
$id = $payload['id']; // 123
}
Creating a Controller
Basic structure
<?php
namespace MyModule\Api;
class ItemController
{
public function __construct() {}
/**
* Item list
* @param array|null $payload Request data
* @return array [data, httpCode]
*/
public function index($payload = null)
{
global $db, $user;
// Your logic here
$items = [];
return [$items, 200];
}
/**
* Fetch one item
*/
public function show($payload = null)
{
global $db;
$id = $payload['id'] ?? null;
if ($id === null) {
return ['Not Found', 404];
}
// Fetch the object
$item = new \MyObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not Found', 404];
}
// Map for the front
$mapping = new dmMyObject();
$data = $mapping->exportMappedData($item);
return [$data, 200];
}
/**
* Create an item
*/
public function create($payload = null)
{
global $db, $user;
$item = new \MyObject($db);
$item->label = $payload['label'];
$item->description = $payload['description'];
$res = $item->create($user);
if ($res < 0) {
return ['Error creating item', 500];
}
return [['id' => $res], 201];
}
/**
* Update an item
*/
public function update($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
$item = new \MyObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not Found', 404];
}
// Update the fields
if (isset($payload['label'])) {
$item->label = $payload['label'];
}
$res = $item->update($user);
if ($res < 0) {
return ['Error updating item', 500];
}
return ['Updated', 200];
}
/**
* Delete an item
*/
public function delete($payload = null)
{
global $db, $user;
$id = $payload['id'] ?? null;
$item = new \MyObject($db);
$res = $item->fetch($id);
if ($res <= 0) {
return ['Not Found', 404];
}
$res = $item->delete($user);
if ($res < 0) {
return ['Error deleting item', 500];
}
return ['Deleted', 200];
}
/**
* Search with filters
*/
public function search($payload = null)
{
global $db;
$filter = $payload['filter'] ?? 'all';
$limit = $payload['limit'] ?? 10;
// Build the SQL query
$sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "myobject";
$sql .= " WHERE 1=1";
if ($filter === 'active') {
$sql .= " AND status = 1";
}
$sql .= " LIMIT " . (int) $limit;
$resql = $db->query($sql);
$items = [];
while ($obj = $db->fetch_object($resql)) {
$item = new \MyObject($db);
$item->fetch($obj->rowid);
$mapping = new dmMyObject();
$items[] = $mapping->exportMappedData($item);
}
return [$items, 200];
}
}
Accessing the logged-in user
The JWT user is available in the payload:
public function update($payload = null)
{
$user = $payload['user']; // Dolibarr User object
dol_syslog("Action by: " . $user->login);
}
See also
- Mapping Dolibarr - React - dm* classes
- Complete Controller example
- SmartAuth - JWT authentication