Skip to content
Laureano Lamonega edited this page Jul 17, 2026 · 4 revisions

Model

The base model is a thin CRUD wrapper around the QueryBuilder. It is not an ORM: there is no relation inference, no attribute casting, no change tracking. You tell the framework what to do instead of it guessing.

Breaking change in v0.10.0

Before v0.10.0 the model inferred the table name from the class name (e.g. Userusers). That behavior was removed. As of v0.10.0 every model must declare its table explicitly:

use Antimonial\Model\Model;

class User extends Model
{
    protected string $table = 'users';   // required since v0.10.0
}

A model without a $table property throws RuntimeException at construction. There is no default and no fallback.

Defining a Model

use Antimonial\Model\Model;

class User extends Model
{
    protected string $table = 'users';
}

Table Name

The table name is whatever you declare in $table. The framework does not transform the class name (no snake_case conversion, no pluralization). If your table is blog_posts, write protected string $table = 'blog_posts';.

class Category extends Model
{
    protected string $table = 'categories';
}

Primary Key

The default primary key is id. Override with:

class User extends Model
{
    protected string $primaryKey = 'user_id';
}

Timestamps

Enable automatic created_at / updated_at:

class User extends Model
{
    protected bool $timestamps = true;
}

CRUD Operations

Find

$user = (new User())->find(42);  // object or null

All

$users = (new User())->all();  // array of objects

Where

$users = (new User())->where('active', true)->get();

Insert

$id = (new User())->insert([
    'name'  => 'John',
    'email' => 'john@example.com',
]);

Update

(new User())->update(42, ['name' => 'Jane']);

Delete

(new User())->delete(42);

Query Builder Access

$qb = (new User())->query();  // Get the underlying QueryBuilder
$user = (new User())->query()->where('email', 'john@example.com')->first();

Clone this wiki locally