Models

Models represent your database tables as PHP objects using Doctrine ORM's powerful entity system.

Creating Models

Generate a model using Craftsman CLI:

php craftsman make:model User

This creates app/Models/User.php:

app/Models/User.php
<?php

namespace App\Models;

use Doctrine\ORM\Mapping as ORM;
use ZephyrPHP\Database\Model;

#[ORM\Entity]
#[ORM\Table(name: 'users')]
class User extends Model
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private int $id;

    #[ORM\Column(type: 'string', length: 255)]
    private string $name;

    #[ORM\Column(type: 'string', length: 255, unique: true)]
    private string $email;

    #[ORM\Column(type: 'string', length: 255)]
    private string $password;

    #[ORM\Column(type: 'datetime')]
    private \DateTime $createdAt;

    // Getters and setters...
}

Column Types

Doctrine supports various column types:

Doctrine Type PHP Type Description
string string VARCHAR field
text string TEXT field for long content
integer int INT field
bigint string BIGINT field
boolean bool BOOLEAN field
decimal string DECIMAL field (precision, scale)
float float FLOAT field
datetime DateTime DATETIME field
date DateTime DATE field
time DateTime TIME field
json array JSON field

Column Options

#[ORM\Column(type: 'string', length: 255)]
private string $name;

#[ORM\Column(type: 'string', unique: true)]
private string $email;

#[ORM\Column(type: 'string', nullable: true)]
private ?string $phone;

#[ORM\Column(type: 'boolean', options: ['default' => false])]
private bool $active = false;

#[ORM\Column(type: 'decimal', precision: 10, scale: 2)]
private string $price;

Querying Models

Finding Records

// Find by primary key
$user = User::find(1);

// Find or fail (throws exception if not found)
$user = User::findOrFail(1);

// Find by criteria
$user = User::findOneBy(['email' => 'john@example.com']);

// Get all records
$users = User::all();

Query Builder

// Simple query
$users = User::query()
    ->where('active', '=', true)
    ->all();

// Multiple conditions
$users = User::query()
    ->where('active', '=', true)
    ->where('role', '=', 'admin')
    ->orderBy('created_at', 'DESC')
    ->all();

// Limit and offset
$users = User::query()
    ->limit(10)
    ->offset(20)
    ->all();

// Count records
$count = User::query()
    ->where('active', '=', true)
    ->count();

// Get first record
$user = User::query()
    ->where('email', '=', 'john@example.com')
    ->first();

Pagination

// Paginate results (15 per page)
$users = User::query()
    ->where('active', '=', true)
    ->paginate(15);

// In your template
{% for user in users.items %}
    {{ user.name }}
{% endfor %}

Page {{ users.currentPage }} of {{ users.totalPages }}
Total: {{ users.total }} users

Creating Records

// Create and save
$user = new User();
$user->setName('John Doe');
$user->setEmail('john@example.com');
$user->setPassword(password_hash('secret', PASSWORD_DEFAULT));
$user->save();

// Using fill method
$user = new User();
$user->fill([
    'name' => 'John Doe',
    'email' => 'john@example.com'
]);
$user->save();

Updating Records

$user = User::find(1);
$user->setName('Jane Doe');
$user->save();

// Or using fill
$user->fill(['name' => 'Jane Doe'])->save();

Deleting Records

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

Relationships

One-to-Many

app/Models/User.php
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;

#[ORM\Entity]
class User extends Model
{
    #[ORM\OneToMany(targetEntity: Post::class, mappedBy: 'author')]
    private Collection $posts;

    public function __construct()
    {
        $this->posts = new ArrayCollection();
    }

    public function getPosts(): Collection
    {
        return $this->posts;
    }
}
app/Models/Post.php
#[ORM\Entity]
class Post extends Model
{
    #[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'posts')]
    #[ORM\JoinColumn(name: 'author_id', referencedColumnName: 'id')]
    private User $author;

    public function getAuthor(): User
    {
        return $this->author;
    }

    public function setAuthor(User $author): void
    {
        $this->author = $author;
    }
}

Many-to-Many

#[ORM\Entity]
class User extends Model
{
    #[ORM\ManyToMany(targetEntity: Role::class)]
    #[ORM\JoinTable(name: 'user_roles')]
    private Collection $roles;

    public function __construct()
    {
        $this->roles = new ArrayCollection();
    }

    public function addRole(Role $role): void
    {
        if (!$this->roles->contains($role)) {
            $this->roles->add($role);
        }
    }
}

Timestamps

Add automatic timestamps to your models:

#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
class User extends Model
{
    #[ORM\Column(type: 'datetime')]
    private \DateTime $createdAt;

    #[ORM\Column(type: 'datetime')]
    private \DateTime $updatedAt;

    #[ORM\PrePersist]
    public function onPrePersist(): void
    {
        $this->createdAt = new \DateTime();
        $this->updatedAt = new \DateTime();
    }

    #[ORM\PreUpdate]
    public function onPreUpdate(): void
    {
        $this->updatedAt = new \DateTime();
    }
}
Doctrine Documentation

For advanced features like inheritance mapping, custom types, and DQL queries, see the Doctrine ORM documentation.