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.
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.
// thinclass erpController extends Controller { public function syncAction(): void { View::forwardToJsonHeader(); $result = \Nibiru\Module\Erp\Plugin\Sync::run(); View::assign(['data' => $result]); }}``````php// fatclass 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é.
2. CMS comme source de contenu
Section intitulée « 2. CMS comme source de contenu »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>3. Analyse basée sur l’observation
Section intitulée « 3. Analyse basée sur l’observation »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() internallyChaque 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.
4. Composition multinavigative
Section intitulée « 4. Composition multinavigative »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.
6. Flux de travail multiétapes via des actions
Section intitulée « 6. Flux de travail multiétapes via des actions »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.
Anti-modèles à éviter
Section intitulée « Anti-modèles à éviter »- 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é. UtilisezView::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.