---
title: "Modelle"
description: "Wie Nibiru Model-Klassen aus Ihrem Datenbankschema automatisch generiert und wie Sie diese erweitern können."
---

Nibiru's Model-Schicht ist *schema-first*. Sie schreiben keine Modelle von Hand – das Framework liest Ihre Datenbank und generiert für jede Tabelle eine PHP-Klasse bei jedem Start.

## Wie die Generierung funktioniert

Wenn `[GENERATOR] database = true` in Ihrer INI-Datei ist, führt der Dispatcher bei jeder Anfrage `new Model(false)` aus, was dazu führt, dass:

1. Verbindet sich mit dem aktiven Treiber.
2. Listet die Tabellen in der konfigurierten Datenbank auf (`information_schema.tables` für PG, `SHOW TABLES` für MySQL).
3. Für jede Tabelle schreibt es eine Datei `application/model/<table>.php`, die eine Klasse enthält, die den relevanten `Db` Adapter erweitert und eine `TABLE` Konstante einbettet, die die Spalten beschreibt.

Erneutes Ausführen ist günstig: Der Generator überschreibt nur dann, wenn das Schema geändert wurde, sodass eingetragene Modelle ihre manuell geschriebenen Methoden beibehalten *wenn* Sie `database = false` nach dem ersten Lauf setzen.
```ini
[GENERATOR]
database          = true     ; regenerate models on each request
database.overwrite = true    ; if false, generator won't touch existing files
```
Im Produktionsumfeld setzen Sie `database = false`, damit die Modelle nicht bei jedem Aufruf neu generiert werden.

## Ein generiertes Modell

Für eine `users` Tabelle mit den Spalten `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);
    }
}
```
Der `Db` Adapter bietet Ihnen einfache CRUD-Hilfsprogramme über `Pdo::` (oder den aktiven Treiber):
```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]);
```
## Benutzerdefinierte Abfragen

Die generierte Klasse ist eine einfache PHP-Klasse – fügen Sie Methoden frei hinzu:
```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 . '%']);
    }
}
```
Wenn der Generator erneut mit `database.overwrite = true` ausgeführt wird, wird diese Datei ersetzt. Behalten Sie benutzerdefinierte Methoden in einer Kindklasse bei oder setzen Sie `database.overwrite = false` nach der ersten Generierung.

## Muster: dünnes Modell + Modul-Plugin

Für komplexe Domänen (Authentifizierung, Abrechnung, Analytik) platzieren die Showcase-Apps die Abfragemethoden auf einem **Modulplugin** anstatt direkt im Modell. Das Plugin besitzt die Geschäftsregeln; das Modell ist nur ein typisierter Handle für die Tabelle:
```
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;
    }
}
```
Dies behält die Controller dünn (`$this->user->isAuthorized()`), während das generierte Modell unverändert bleibt.

## Multifahrer-Hinweise

Der generierte `Db` Adapter ist treiberspezifisch. Wenn Sie von MySQL zu PostgreSQL wechseln, müssen Sie die Modelle neu generieren, sodass sie die richtige Basisklasse erweitern:

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

Die gleichen Abfragehilfen, aber ein anderer Adapter im Hintergrund.

## Wann der Generator übersprungen werden soll

Schreibgeschützte Datenbanken, Lieferanten-Systeme oder jedes Schema, das Sie nicht steuern: Setzen Sie `database = false`, schreiben Sie minimale Modellklassen, die den tatsächlich verwendeten Spalten entsprechen, und übertragen Sie sie in Ihr Versionskontrollsystem.
