Aller au contenu
Nibiru docsv0.9.2

Modèles issus de la production

Modèles concrèts utilisés dans les applications Nibiru en production — prêts à être copiés et collés.

Stable Reading time ~ 3 min Edit on GitHub

Sept modèles qui se répandent régulièrement dans les applications de production Nibiru. Chacun est petit, prêt à être copié-collé et basé sur une base de code réelle.

1. Contrôleur mince → délégation au module-plugin

Section intitulée « 1. Contrôleur mince → délégation au module-plugin »

Gardez les contrôleurs petits. Poussez la logique dans les modules des plug-ins.

// thin
class erpController extends Controller {
public function syncAction(): void {
View::forwardToJsonHeader();
$result = \Nibiru\Module\Erp\Plugin\Sync::run();
View::assign(['data' => $result]);
}
}
``````php
// fat
class Sync extends Erp {
public static function run(): array {
$svc = AlphaplanSyncService::getInstance();
try {
return ['success' => true, 'changes' => $svc->syncAbDocuments()];
} catch (\Throwable $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}

Le contrôleur est examinable en 5 secondes ; le plug-in est testable par unité.

Découpez le texte du formatage. Les éditeurs mettent à jour le contenu via l’interface utilisateur du module CMS ; les modèles restent sous la responsabilité des développeurs.

public function pageAction() {
$path = $this->getController() . '/' . $this->getRequest('_action', 'page');
foreach (Cms::init($this->getController())
->loadCmsTemplateTextsByControllerPath($path, $this->language)
as $t)
{
View::assign([
$t['cms_template_texts_text_identifier']
=> $t['cms_template_texts_text_content']
]);
}
}

Les modèles font référence aux identifiants comme s’ils étaient des variables ordinaires :

<h1>{$hero_title}</h1>
<p>{$hero_intro}</p>

Plusieurs suivi sans couplage avec le contrôleur.

$analytics = new Analytics();
$analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible());
$analytics->trackPageView(); / calls notify() internally

Chaque observateur appelle uniquement les champs dont il s’occupe avec sa méthode update($subject). Ajouter un suivi est une modification d’une seule ligne.

Construisez des pages avec plusieurs tableaux de navigation nommés au lieu d’une structure monolithique.

public function navigationAction() {
foreach (['head', 'main', 'social', 'footer'] as $name) {
JsonNavigation::getInstance()->loadJsonNavigationArray($name);
}
}
``````smarty
<header>{include file="navigation.tpl" array=$head}</header>
<aside>{include file="navigation.tpl" array=$main}</aside>
<footer>{include file="navigation.tpl" array=$footer}</footer>

Chaque fichier JSON est petit, limité, facile à éditer et sans conflit dans les demandes de tirage (PR).

5. Points d’accès JSON avec forwardToJsonHeader

Section intitulée « 5. Points d’accès JSON avec forwardToJsonHeader »

Contrat standard pour AJAX :

public function searchAction() {
View::forwardToJsonHeader();
$q = trim($_REQUEST['q'] ?? '');
if (strlen($q) < 2) {
View::assign(['data' => ['results' => []]]);
return;
}
View::assign(['data' => [
'results' => MachineryScout::index()->search($q),
]]);
}

Les en-têtes sont définis automatiquement ; aucune modification manuelle de header('Content-Type: application/json') n’est nécessaire.

Machines d’état mappées sur les actions des contrôleurs :

class quotesController extends Controller {
public function pageAction() { /* list view */ }
public function detailAction() { /* one quote */ }
public function acceptAction() { /* state transition: open → accepted */ }
public function rejectAction() { /* state transition: open → rejected */ }
public function archiveAction() { /* state transition: any → archived */ }
}

/citations/accepter/42 exécute acceptAction() avec $_REQUEST['id'] = 42. Chaque transition est une petite action ; la persistance et la notification passent par un plugin QuotesService.

7. Modèles basés sur le schéma avec une méthode personnalisée par intention

Section intitulée « 7. Modèles basés sur le schéma avec une méthode personnalisée par intention »

Générez le modèle à partir du schéma, puis ajoutez des méthodes nommées par l’intention qui encapsulent vos requêtes :

class users extends Db
{
const TABLE = ['table' => 'users', 'field' => [/* … */]];
public function __construct() { self::initTable(self::TABLE); }
public function findByLogin(string $login): ?array {
return Pdo::fetchRow('SELECT * FROM users WHERE user_login = :l',
[':l' => $login]) ?: null;
}
public function activeStandardUsers(): array {
return Pdo::fetchAll(
'SELECT * FROM users WHERE user_account_active = 1 AND user_role = :r',
[':r' => 'standard']
);
}
}

L’avenir-vous qui lit le point d’appel voit findByLogin($login) — l’intention — et non du code SQL brut.

  • Fuite de tampon statique dans les formulaires. Utilisez toujours Form::create() avant la construction.
  • Logique dans navigationAction(). Elle s’exécute à chaque requête, y compris les points de terminaison JSON.
  • Assignation massive de View::assign() sans un tableau structuré. Utilisez View::assign(['…']) une seule fois.
  • Routes personnalisées pour ce que fait déjà l’URL SEO. /products/<slug>/<id> est gratuit.
  • Modification des modèles générés. Ils sont écrabouillés. Méthodes personnalisées → classe enfant ou database.overwrite = false.