Skip to main content

Overview

Models are PHP classes that represent database tables and provide methods for creating, reading, updating, and deleting records (CRUD operations).

Defining Models

Basic Model

Extend the Aeros\Src\Classes\Model base class:
namespace App\Models;

use Aeros\Src\Classes\Model;

class User extends Model
{
    protected $table = 'users';
    protected $primary = 'id';
    protected $fillable = ['name', 'email', 'status'];
}

Model Properties

table
string
default:"null"
The database table name. Auto-detected from class name if not set.
primary
string
default:"'id'"
The primary key column name.
fillable
array
default:"[]"
Columns that can be mass-assigned (for security).
guarded
array
default:"[]"
Columns that are protected from mass-assignment.

Auto-Detected Table Names

If $table is not defined, the model automatically uses the pluralized, lowercase class name:
class User extends Model { }     // → users
class Post extends Model { }     // → posts
class Category extends Model { } // → categories

Creating Records

Create Single Record

$user = User::create([
    'name' => 'John Doe',
    'email' => '[email protected]',
    'status' => 'active'
]);

echo $user->id; // Auto-generated ID

Create Multiple Records

$users = User::createMany([
    ['name' => 'Alice', 'email' => '[email protected]'],
    ['name' => 'Bob', 'email' => '[email protected]'],
    ['name' => 'Charlie', 'email' => '[email protected]']
]);

// Returns array of created User models

Finding Records

Find by ID

// Find user with id = 1
$user = User::find(1);

// Returns User model or null
if ($user) {
    echo $user->name;
}

Find by Conditions

// Single condition (key = value)
$user = User::find([
    ['email', '[email protected]']
]);

// Multiple conditions with operators
$users = User::find([
    ['status', '=', 'active'],
    ['role', '=', 'admin']
]);

// With OR conditions
$users = User::find([
    ['status', '=', 'active', 'OR'],
    ['status', '=', 'pending', 'OR'],
    ['status', '=', 'verified']
]);

Find with Specific Columns

// Only fetch id, name, email
$user = User::find(1, ['id', 'name', 'email']);

Condition Formats

The find() method supports multiple condition formats:
// Format 1: [column, value]
['email', '[email protected]']  // WHERE email = ?

// Format 2: [column, operator, value]
['age', '>', 18]  // WHERE age > ?

// Format 3: [column, operator, value, logical]
['status', '=', 'active', 'OR']  // WHERE status = ? OR

// Format 4: Associative array
[['status' => 'active']]  // WHERE status = ?

Updating Records

Update by Instance

$user = User::find(1);
$user->name = 'Jane Doe';
$user->email = '[email protected]';
$user->save();

Update with Method

// Update specific records
User::update(
    ['status' => 'inactive'],  // New values
    [['role', '=', 'guest']]   // WHERE conditions
)->commit();

Update and Commit

Changes are staged until commit() is called:
$user = User::find(1);
$user->status = 'verified';
$result = $user->save(); // Calls commit() internally

Deleting Records

Delete Instance

$user = User::find(1);
$user->delete()->commit();

Delete by Condition

// Delete all inactive users
User::delete([
    ['status', '=', 'inactive']
])->commit();

Property Access

Getting Values

$user = User::find(1);

echo $user->id;
echo $user->name;
echo $user->email;

Setting Values

$user = User::find(1);

// Set individual properties
$user->name = 'New Name';
$user->email = '[email protected]';

// Save changes
$user->save();

Protected Properties

Primary keys and guarded properties cannot be updated
class User extends Model
{
    protected $primary = 'id';
    protected $guarded = ['role', 'permissions'];
}

$user = User::find(1);

// ❌ Throws InvalidArgumentException
$user->id = 999;

// ❌ Throws InvalidArgumentException
$user->role = 'admin';

Fillable vs Guarded

Using Fillable (Whitelist)

class User extends Model
{
    protected $fillable = ['name', 'email', 'bio'];
}

// ✅ Only name, email, bio can be set
User::create([
    'name' => 'John',
    'email' => '[email protected]',
    'bio' => 'Developer',
    'role' => 'admin'  // Ignored (not in fillable)
]);

Using Guarded (Blacklist)

class User extends Model
{
    protected $guarded = ['role', 'permissions'];
}

// ✅ All columns except role and permissions can be set
User::create([
    'name' => 'John',
    'email' => '[email protected]',
    'role' => 'admin'  // Ignored (in guarded)
]);

Eager Loading

Prevent N+1 query problems by eager loading relationships:
// Load user with their plans
$user = User::with('plans')->find(1);

// Access relationship without additional query
foreach ($user->plans as $plan) {
    echo $plan->name;
}

// Load multiple relationships
$user = User::with(['plans', 'profile', 'roles'])->find(1);
See Relationships for more details.

Model Methods

Instance Methods

save()
mixed
Save changes to the current model instance
$user->name = 'Updated';
$user->save();
commit()
mixed
Commit pending INSERT, UPDATE, or DELETE operations
$user->delete()->commit();
getTableNameFromModel()
string
Get the table name for this model
echo $user->getTableNameFromModel(); // 'users'
getPrimaryKey()
string
Get the primary key column name
echo $user->getPrimaryKey(); // 'id'

Static Methods

find()
Model|array|null
Find records by ID or conditions
User::find(1);
User::find([['status', 'active']]);
create()
Model
Create a new record
User::create(['name' => 'John']);
createMany()
array
Create multiple records at once
User::createMany([...]);
update()
Model
Update records matching conditions
User::update(['status' => 'active'], [['role', 'user']]);
delete()
Model
Delete records matching conditions
User::delete([['status', 'inactive']]);
with()
Model
Eager load relationships
User::with('plans')->find(1);

Error Handling

Invalid Property

$user = User::find(1);

try {
    echo $user->nonexistent;
} catch (InvalidArgumentException $e) {
    // Property "nonexistent" is not mapped to any column on "users" table
}

Primary Key Update

try {
    $user->id = 999;
} catch (InvalidArgumentException $e) {
    // Primary key "id" cannot be updated
}

Unknown Column

try {
    $user->unknown_field = 'value';
} catch (InvalidArgumentException $e) {
    // Property "unknown_field" is not mapped to any column on "users" table
}

Advanced Usage

Custom Table Name

class User extends Model
{
    protected $table = 'app_users';  // Use custom table name
}

Custom Primary Key

class User extends Model
{
    protected $primary = 'user_id';  // Use custom primary key
}

Combining with Query Builder

$users = User::query()
    ->where('status', 'active')
    ->orderBy('created_at', 'DESC')
    ->limit(10)
    ->get();
See Query Builder for more details.

Best Practices

Always define fillable or guarded to prevent mass-assignment vulnerabilities
Use eager loading to avoid N+1 query problems
Validate data before creating or updating records
Use transactions for operations that modify multiple tables
Never trust user input - always validate and sanitize

Next Steps

Relationships

Define relationships between models

Query Builder

Build complex database queries

Build docs developers (and LLMs) love