0

Defining models — $table, $primaryKey, $timestamps, $fillable, $guarded

Beginner5 min read·lv-12-002

Concept

Eloquent model definition involves several class-level properties that control how the model interacts with the database. Getting these right avoids subtle bugs and unexpected behavior.

$table: Override the table name if it doesn't follow the plural snake_case convention. Userusers is automatic. UserProfileuser_profiles is automatic. For non-standard names: protected $table = 'wp_users'.

$primaryKey: Default is 'id' (integer). Change if your table uses a different column: protected $primaryKey = 'user_id'. For string/UUID primary keys: also set public $incrementing = false and protected $keyType = 'string'.

$timestamps: Default true — Eloquent manages created_at and updated_at columns. Set to false to disable. CREATED_AT and UPDATED_AT constants override the column names.

$fillable and $guarded: Mass assignment protection. $fillable is an allowlist of columns that can be mass-assigned. $guarded is a blocklist. Never use $guarded = [] in production.

$hidden: Columns excluded from toArray() / JSON serialization. Typically password, remember_token, api_key.

$visible: Opposite of $hidden — only these columns appear in toArray(). Use one or the other, not both.

$casts: Type casting — PHP types for model attributes. 'settings' => 'array' → decodes JSON to array. 'published_at' => 'datetime' → Carbon instance. 'is_active' => 'boolean'.

$appends: Adds accessor/computed properties to toArray() output. $appends = ['full_name'] → requires getFullNameAttribute().

Code Example

php
<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class BlogPost extends Model
{
    // Non-standard table name
    protected $table = 'blog_entries';

    // Non-standard primary key
    protected $primaryKey = 'post_id';

    // Custom timestamp column names
    const CREATED_AT = 'created_on';
    const UPDATED_AT = 'modified_on';

    // Mass assignment — only these fields via create()/update()
    protected $fillable = [
        'title', 'slug', 'content', 'excerpt',
        'author_id', 'category_id', 'published_at',
    ];

    // Fields hidden from toArray() / JSON
    protected $hidden = ['raw_html', 'internal_notes'];

    // Type casting
    protected $casts = [
        'published_at' => 'datetime',         // Carbon instance
        'is_featured'  => 'boolean',          // int → bool
        'tags'         => 'array',            // JSON string ↔ PHP array
        'metadata'     => 'array',            // same
        'price'        => 'decimal:2',        // string → float with 2 decimals
        'settings'     => \App\Casts\SettingsCast::class, // custom cast
    ];

    // Computed/accessor attributes included in toArray()
    protected $appends = ['reading_time', 'is_published'];

    // Accessors (PHP 8.2 syntax with get prefix and Attribute class)
    public function readingTime(): \Illuminate\Database\Eloquent\Casts\Attribute
    {
        return \Illuminate\Database\Eloquent\Casts\Attribute::get(fn() =>
            ceil(str_word_count($this->content) / 200) . ' min read'
        );
    }

    public function isPublished(): \Illuminate\Database\Eloquent\Casts\Attribute
    {
        return \Illuminate\Database\Eloquent\Casts\Attribute::get(fn() =>
            $this->published_at !== null && $this->published_at->isPast()
        );
    }
}

// UUID primary key model
class Event extends Model
{
    protected $primaryKey = 'id';
    public $incrementing = false;       // UUIDs don't auto-increment
    protected $keyType = 'string';      // UUID is a string

    protected static function boot(): void
    {
        parent::boot();
        static::creating(fn($model) => $model->id = (string) \Illuminate\Support\Str::uuid());
    }
}