Skip to content
Nibiru docsv0.9.2

Models

How Nibiru auto-generates model classes from your database schema, and how to extend them.

Stable Reading time ~ 3 min Edit on GitHub

Nibiru’s model layer is schema-first. You don’t write models by hand — the framework reads your database and generates one PHP class per table on every boot.

When [GENERATOR] database = true in your INI, the dispatcher runs new Model(false) on each request, which:

  1. Connects with the active driver.
  2. Lists tables in the configured database (information_schema.tables for PG, SHOW TABLES for MySQL).
  3. For each table, writes application/model/<table>.php containing a class that extends the relevant Db adapter and embeds a TABLE constant describing the columns.

Re-running is cheap: the generator overwrites only when the schema has changed, so checked-in models keep their handwritten methods if you turn database = false after the first run.

[GENERATOR]
database = true ; regenerate models on each request
database.overwrite = true ; if false, generator won't touch existing files

In production set database = false so models aren’t regenerated on every hit.

For a users table with columns user_id, user_login, user_pass, user_email:

<?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);
}
}

The Db adapter gives you simple CRUD helpers via Pdo:: (or the active driver):

$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]);

The generated class is a plain PHP class — add methods freely:

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 . '%']);
}
}

If the generator runs again with database.overwrite = true it will replace this file. Keep custom methods in a child class or set database.overwrite = false after first generation.

For complex domains (auth, billing, analytics), the showcase apps put query methods on a module plugin rather than the bare model. The plugin owns the business rules; the model is just a typed handle to the table:

application/module/users/
├── plugins/
│ └── user.php # User::isAuthorized(), User::loginForm(), …
└── traits/
└── userForm.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 keeps controllers thin ($this->user->isAuthorized()) while leaving the generated model untouched.

The generated Db adapter is driver-specific. If you switch from MySQL to PostgreSQL, regenerate models so they extend the right base class:

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

Same query helpers, different adapter under the hood.

Read-only databases, vendor systems, or any schema you don’t control: set database = false, hand-write minimal model classes that match the columns you actually use, and check them in.