---
title: "Modèles issus de la production"
description: "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

Gardez les contrôleurs petits. Poussez la logique dans les modules des plug-ins.
```php
// 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é.

## 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.
```php
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 :
```smarty
<h1>{$hero_title}</h1>
<p>{$hero_intro}</p>
```
## 3. Analyse basée sur l'observation

Plusieurs suivi sans couplage avec le contrôleur.
```php
$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.

## 4. Composition multinavigative

Construisez des pages avec plusieurs tableaux de navigation nommés au lieu d'une structure monolithique.
```php
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`

Contrat standard pour AJAX :
```php
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

Machines d'état mappées sur les actions des contrôleurs :
```php
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

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 :
```php
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

- **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`.
