---
title: "生産からのパターン"
description: "Nibiruアプリケーションで使用されている具体的なパターン — コピーペースト可能なものです。"
---

ニブルの生産アプリケーションで繰り返し現れる7つのパターン。それぞれが小さく、コピー＆ペーストできるもので、実際のコードベースに基づいています。

## 1. 細かいコントローラー → モジュールプラグインの委譲

コントローラーを小さく保ちましょう。ロジックはモジュールプラグインに押し込んでください。
```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()];
        }
    }
}
```
コントローラーは5秒でレビューできます；プラグインは単体テスト可能です。

## 2. コンテンツソースとしてのCMS

テキストとレイアウトを分離します。エディターはCMSモジュールのUI経由でコピーアップデートを行います；テンプレートは開発者所有のままです。
```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']
        ]);
    }
}
```
テンプレートは識別子を通常の変数のように参照します。
```smarty
<h1>{$hero_title}</h1>
<p>{$hero_intro}</p>
```
## 3. オブザーバー駆動型分析

複数のトラッカーとコントローラーの結合なし。
```php
$analytics = new Analytics();
$analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible());
$analytics->trackPageView();   // calls notify() internally
```
各観察者の `update($subject)` は、関心のあるフィールドのみを取得します。トラッカーの追加は1行の変更です。

## 4. マルチナビゲーション構成

複数の名前付きナビゲーション配列を使用してページを構築する代わりに、1つのモノリス構造を使用します。
```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>
```
各 JSON ファイルは小さく、スコープが明確で、編集しやすいです。また、PR での競合もありません。

## 5. JSON エンドポイントと `forwardToJsonHeader`

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),
    ]]);
}
```
ヘッダーは自動的に設定されます；手動で `header('Content-Type: application/json')` を行わないでください。

## 6. アクションを経由したマルチステージワークフロー

コントローラーアクションにマッピングされたステートマシン：
```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 */ }
}
```
`/quotes/accept/42` は `acceptAction()` を実行し、`$_REQUEST['id'] = 42` です。各トランジションは小さなアクションであり、永続化と通知は `QuotesService` プラグインを介して行われます。

## 7. スキーマファーストモデルと各インテントごとの1つのカスタムメソッド

スキーマからモデルを生成し、その後クエリをラップする意図名のメソッドを追加します。
```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']
        );
    }
}
```
将来的な自分は、`findByLogin($login)` — 意図 — ではなく生のSQLを読むことになります。

## 避けるべきアンチパターン

- **フォームでの静的バッファリー漏洩。** 常に `Form::create()` を実行してから構築してください。
- **`navigationAction()` 内のロジック。** すべてのリクエストで実行され、JSON エンドポイントも含まれます。
- **構造化された配列なしでの大量の `View::assign()`。** 一度に `View::assign(['…'])` を使用してください。
- **SEO URL がすでに処理するカスタムルート。** `/products/<slug>/<id>` は無料です。
- **生成されたモデルの編集。** それらは上書きされます。カスタムメソッド → 子クラスまたは `database.overwrite = false`。
