---
title: "モデル"
description: "Nibiruは、データベーススキーマからモデルクラスを自動生成する方法と、それらを拡張する方法について説明します。"
---

Nibiruのモデル層は*スキーマファースト*です。手でモデルを書くことはありません — フレームワークはデータベースを読み取り、起動時に各テーブルごとに1つのPHPクラスを生成します。

## 生成の仕組み

`[GENERATOR] database = true` が INI ファイルに指定されている場合、ディスパッチャは各リクエストで `new Model(false)` を実行します。これにより：

1. アクティブなドライバーに接続します。
2. 設定されたデータベース内のテーブルをリスト表示します（PGの場合: `information_schema.tables`、MySQLの場合: `SHOW TABLES`）。
3. 各テーブルについて、`application/model/<table>.php` にクラスを作成し、該当する `Db` アダプターを拡張し、列を記述した `TABLE` 定数を埋め込みます。

再実行は安価です：ジェネレーターはスキーマが変更された場合のみ上書きするため、チェックインされたモデルは手で書いたメソッドを保持します*ただし、最初の実行後 `database = false` を設定すると。
```ini
[GENERATOR]
database          = true     ; regenerate models on each request
database.overwrite = true    ; if false, generator won't touch existing files
```
本番環境では `database = false` を設定して、モデルが毎回リジェネレートされないようにします。

## 生成されたモデル

`users` テーブルに `user_id, user_login, user_pass, user_email` という列がある場合:
```php
<?php
namespace Nibiru\Model;
use Nibiru\Adapter\MySQL\Db;

class users extends Db
{
    const TABLE = [
        'table' => 'users',
        'field' => [
            'user_id'    => 'user_id',
            'user_login' => 'user_login',
            'user_pass'  => 'user_pass',
            'user_email' => 'user_email',
        ],
    ];

    public function __construct() {
        self::initTable(self::TABLE);
    }
}
```
`Db` アダプターは、`Pdo::`（またはアクティブなドライバー）を介して単純な CRUD ヘルパーを提供します。
```php
$users = new \Nibiru\Model\users();

// Read by primary key
$row = \Nibiru\Pdo::fetchRowInArrayById('users', ['user_id' => 42]);

// Read all
$all = \Nibiru\Pdo::fetchAll('SELECT * FROM users WHERE user_account_active = 1');

// Insert
\Nibiru\Pdo::insert('users', [
    'user_login' => 'marduk',
    'user_email' => 'marduk@nibiru.local',
]);

// Update
\Nibiru\Pdo::update('users',
    ['user_email' => 'new@nibiru.local'],
    ['user_id' => 42]);

// Delete
\Nibiru\Pdo::delete('users', ['user_id' => 42]);
```
## カスタムクエリ

生成されたクラスはプレーンな PHP クラスです — 適便にメソッドを追加してください：
```php
namespace Nibiru\Model;
use Nibiru\Adapter\MySQL\Db;
use Nibiru\Pdo;

class documentation extends Db
{
    const TABLE = [
        'table' => 'documentation',
        'field' => [
            'id' => 'id', 'title' => 'title', 'slug' => 'slug',
            'content' => 'content', 'category' => 'category',
            'version' => 'version',
        ],
    ];

    public function __construct() { self::initTable(self::TABLE); }

    public function getBySlug(string $slug): ?array {
        return Pdo::fetchRowInArrayById(
            self::TABLE['table'],
            [self::TABLE['field']['slug'] => $slug]
        ) ?: null;
    }

    public function search(string $query): array {
        $sql = 'SELECT * FROM ' . self::TABLE['table']
             . ' WHERE title LIKE :q OR content LIKE :q ORDER BY title';
        return Pdo::fetchAll($sql, [':q' => '%' . $query . '%']);
    }
}
```
ジェネレーターが再度実行されると、`database.overwrite = true` の場合、このファイルは上書きされます。カスタムメソッドは子クラスに保持するか、最初の生成後には `database.overwrite = false` を設定してください。

## パターン: 細かいモデル + モジュールプラグイン

複雑なドメイン（認証、課金、分析）では、デモンストレーションアプリケーションはクエリメソッドを**モジュールプラグイン**に配置し、単なるモデルではなくします。プラグインがビジネスルールを持ち、モデルは単にテーブルへの型付けされたハンドルだけです：
```
application/module/users/
├── plugins/
│   └── user.php         # User::isAuthorized(), User::loginForm(), …
└── traits/
    └── userForm.php
``````php
namespace Nibiru\Module\Users\Plugin;
use Nibiru\Model\users;

class User {
    private users $usersModel;
    public function __construct() { $this->usersModel = new users(); }

    public function isAuthorized(): bool {
        return isset($_SESSION['auth']['user_id']);
    }

    public function findByLogin(string $login): ?array {
        return \Nibiru\Pdo::fetchRow(
            'SELECT * FROM users WHERE user_login = :login',
            [':login' => $login]
        ) ?: null;
    }
}
```
これにより、コントローラーは薄くなりますが（`$this->user->isAuthorized()`）、生成されたモデルは変更されません。

## マルチドライバーの注意点

生成された `Db` アダプターはドライバーやせきにあります。MySQL から PostgreSQL に切り替える場合は、モデルを再生成して正しい基底クラスを継承するようにしてください。

- MySQL / PDO → `Nibiru\Adapter\MySQL\Db`
- PostgreSQL (libpq) → `Nibiru\Adapter\PostgreSQL\Db`
- ODBC → `Nibiru\Adapter\Odbc\Db`

同じクエリヘルパーを使用していますが、内部で異なるアダプターを使用しています。

## ジェネレーターをスキップする場合

読み取り専用データベース、ベンダーのシステム、またはあなたがコントロールできないスキーマの場合：`database = false` を設定し、実際に使用する列に一致する最小限のモデルクラスを手書きして、チェックインしてください。
