Skip to main content

Introduction

Eloquent is Lyger’s implementation of the Active Record pattern, providing an elegant and expressive way to interact with your database. Each database table has a corresponding Model class that is used to interact with that table.

Defining Models

Basic Model Definition

Create a model by extending the Lyger\Database\Model class:
app/Models/User.php
<?php

namespace App\Models;

use Lyger\Database\Model;

class User extends Model
{
    protected string $table = 'users';
    protected string $primaryKey = 'id';
    protected array $fillable = ['name', 'email', 'password'];
    protected array $hidden = ['password'];
    protected bool $timestamps = true;
}
If you don’t specify a $table property, Eloquent will automatically use the lowercase, pluralized class name (e.g., User becomes users).

Model Properties

Table Name

// Explicitly set table name
protected string $table = 'users';

Primary Key

// Default is 'id'
protected string $primaryKey = 'id';

// Custom primary key
protected string $primaryKey = 'user_id';

Fillable Attributes

Define which attributes can be mass-assigned:
protected array $fillable = ['name', 'email', 'password', 'status'];
Only attributes listed in $fillable can be set via create() or fill() methods. This protects against mass-assignment vulnerabilities.

Hidden Attributes

Hide attributes when converting to array or JSON:
protected array $hidden = ['password', 'secret_token'];

Timestamps

// Enable automatic timestamp management (default: true)
protected bool $timestamps = true;

// Disable timestamps
protected bool $timestamps = false;
When enabled, Eloquent automatically manages created_at and updated_at columns.

Attribute Casting

Cast attributes to specific types:
protected array $casts = [
    'id' => 'int',
    'email_verified' => 'bool',
    'price' => 'float',
    'options' => 'array',
    'metadata' => 'json',
    'published_at' => 'datetime'
];
Supported casts: int, integer, float, double, bool, boolean, string, array, json, datetime, date

Retrieving Models

Find by Primary Key

// Find by ID (returns null if not found)
$user = User::find(1);

// Find or throw exception
$user = User::findOrFail(1); // Throws RuntimeException if not found

Retrieving All Records

$users = User::all();

foreach ($users as $user) {
    echo $user->name;
}

Using Query Builder Methods

All Query Builder methods are available on models:
// Where clauses
$activeUsers = User::query()
    ->where('status', '=', 'active')
    ->get();

// Ordering
$recentUsers = User::query()
    ->latest('created_at')
    ->limit(10)
    ->get();

// Multiple conditions
$admins = User::query()
    ->where('role', '=', 'admin')
    ->whereNotNull('email_verified_at')
    ->orderBy('name', 'ASC')
    ->get();

Creating Models

Using Create Method

$user = User::create([
    'name' => 'John Doe',
    'email' => 'john@example.com',
    'password' => password_hash('secret', PASSWORD_DEFAULT)
]);

Using Save Method

$user = new User();
$user->name = 'Jane Doe';
$user->email = 'jane@example.com';
$user->password = password_hash('secret', PASSWORD_DEFAULT);
$user->save();

Using Fill Method

$user = new User();
$user->fill([
    'name' => 'Bob Smith',
    'email' => 'bob@example.com'
]);
$user->password = password_hash('secret', PASSWORD_DEFAULT);
$user->save();
The create() and fill() methods only work with attributes listed in the $fillable property.

Updating Models

Update Using Save

$user = User::find(1);
$user->name = 'Updated Name';
$user->email = 'newemail@example.com';
$user->save();

Mass Update via Query

User::query()
    ->where('status', '=', 'pending')
    ->update(['status' => 'active']);

Deleting Models

Delete Single Model

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

Delete via Query

User::query()
    ->where('status', '=', 'inactive')
    ->where('last_login', '<', date('Y-m-d', strtotime('-1 year')))
    ->delete();

Accessing Attributes

Magic Properties

$user = User::find(1);

// Get attributes
echo $user->name;
echo $user->email;

// Set attributes
$user->name = 'New Name';
$user->status = 'active';

Array Access

// Check if attribute exists
if (isset($user->email)) {
    echo $user->email;
}

// Unset attribute
unset($user->temporary_field);

Serialization

To Array

$user = User::find(1);
$array = $user->toArray();

// Hidden attributes are excluded
// Casted attributes are converted to their target types

To JSON

$user = User::find(1);
$json = $user->toJson();

// Or with options
$json = $user->toJson(JSON_PRETTY_PRINT);

Collections

Eloquent returns a Collection instance for multi-record results:
$users = User::all();

// Collection methods
$count = $users->count();
$first = $users->first();
$last = $users->last();
$isEmpty = $users->isEmpty();

// Map over collection
$names = $users->map(fn($user) => $user->name);

// Filter collection
$admins = $users->filter(fn($user) => $user->role === 'admin');

// Pluck specific attribute
$emails = $users->pluck('email');

// Where on collection
$active = $users->where('status', '=', 'active');

// Sort collection
$sorted = $users->sortBy('name');
$sortedDesc = $users->sortByDesc('created_at');

// Take first N items
$topTen = $users->take(10);

// Convert to array/JSON
$array = $users->toArray();
$json = $users->toJson();

Model Methods

Getting the Primary Key

$user = User::find(1);
$id = $user->getKey(); // Returns primary key value
$keyName = $user->getPrimaryKey(); // Returns 'id'

Getting the Table Name

$tableName = $user->getTableName();
// Or statically
$tableName = User::getTable();

Complete Example

Here’s a comprehensive example demonstrating Eloquent usage:
app/Models/Post.php
<?php

namespace App\Models;

use Lyger\Database\Model;

class Post extends Model
{
    protected string $table = 'posts';
    
    protected array $fillable = [
        'title',
        'slug',
        'content',
        'status',
        'user_id',
        'published_at'
    ];
    
    protected array $hidden = [
        'deleted_at'
    ];
    
    protected array $casts = [
        'id' => 'int',
        'user_id' => 'int',
        'published' => 'bool',
        'published_at' => 'datetime',
        'metadata' => 'json'
    ];
    
    protected bool $timestamps = true;
}
use App\Models\Post;

// Create a new post
$post = Post::create([
    'title' => 'Getting Started with Lyger',
    'slug' => 'getting-started-with-lyger',
    'content' => 'Lyger is a modern PHP framework...',
    'status' => 'published',
    'user_id' => 1,
    'published_at' => date('Y-m-d H:i:s')
]);

// Find and update
$post = Post::find(1);
$post->title = 'Updated Title';
$post->save();

// Query posts
$publishedPosts = Post::query()
    ->where('status', '=', 'published')
    ->whereNotNull('published_at')
    ->latest('published_at')
    ->limit(10)
    ->get();

// Iterate over collection
foreach ($publishedPosts as $post) {
    echo "{$post->title} - {$post->published_at}\n";
}

// Get draft posts
$drafts = Post::query()
    ->where('status', '=', 'draft')
    ->where('user_id', '=', $userId)
    ->orderBy('created_at', 'DESC')
    ->get();

// Count posts
$totalPosts = Post::query()->count();
$publishedCount = Post::query()
    ->where('status', '=', 'published')
    ->count();

// Delete old drafts
Post::query()
    ->where('status', '=', 'draft')
    ->where('created_at', '<', date('Y-m-d', strtotime('-30 days')))
    ->delete();

Best Practices

1

Use Mass Assignment Protection

Always define $fillable to protect against mass-assignment vulnerabilities:
protected array $fillable = ['name', 'email', 'status'];
2

Hide Sensitive Data

Use $hidden to exclude sensitive attributes from arrays and JSON:
protected array $hidden = ['password', 'api_token'];
3

Use Type Casting

Cast attributes to appropriate types for better type safety:
protected array $casts = [
    'id' => 'int',
    'active' => 'bool',
    'options' => 'array'
];
4

Enable Timestamps

Let Eloquent manage timestamps automatically:
protected bool $timestamps = true;

Next Steps

Relationships

Define relationships between your models

Query Builder

Learn more about the underlying Query Builder