More Eloquent
An Eloquent model is more than access to a table. It is where everything about the data goes: common query conditions, value conversion, deletion rules. That keeps controllers short.
Query scopes
A condition repeated in several places moves into the model. The method is marked with the Scope attribute:
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
#[Scope]
protected function published(Builder $query): void
{
$query->where('is_published', true);
}
}$posts = Post::published()->latest()->get();Before Laravel 12 the method is named scopePublished and called the same way — published().
Casting types
The database returns everything as strings and numbers. The casts() method says what to turn the columns into:
class Post extends Model
{
protected function casts(): array
{
return [
'is_published' => 'boolean',
'published_at' => 'datetime',
'settings' => 'array',
'price' => 'decimal:2',
];
}
}Now $post->published_at is a date object with methods, and $post->settings is an array stored in the database as JSON.
echo $post->published_at->format('d.m.Y');
echo $post->published_at->diffForHumans();Computed properties
An accessor changes a value when it is read, a mutator when it is written:
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
protected function name(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value),
set: fn (string $value) => strtolower($value),
);
}
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->first_name} {$this->last_name}",
);
}
}$user->full_name works like a regular property although the table has no such column.
Soft deletes
With the SoftDeletes trait a record is not deleted but marked with a date in the deleted_at column. Regular queries no longer see it:
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}In a migration the column is created with $table->softDeletes().
$post->delete();
Post::withTrashed()->get(); // including deleted ones
Post::onlyTrashed()->get(); // only deleted ones
$post->restore();
$post->forceDelete(); // delete for goodCreate or update
$user = User::firstOrCreate(
['email' => 'anna@example.com'],
['name' => 'Anna'],
);
$setting = Setting::updateOrCreate(
['key' => 'theme'],
['value' => 'dark'],
);The first array is the search condition, the second holds the values for the new or updated record.
Model events
A model announces its changes. Handlers are registered in the booted() method:
use Illuminate\Support\Str;
class Post extends Model
{
protected static function booted(): void
{
static::creating(function (Post $post) {
$post->slug = Str::slug($post->title);
});
}
}There are the events creating, created, updating, updated, deleting, deleted.
Common mistake
Events do not fire for mass queries. Post::where('user_id', 5)->delete() removes the rows with one query and does not call the deleting handlers. If you need them, delete the models one by one:
Post::where('user_id', 5)->get()->each->delete();