Defining models — $table, $primaryKey, $timestamps, $fillable, $guarded
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. User → users is automatic. UserProfile → user_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
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());
}
}