Skip to main content

What are model events?

Eloquent models automatically fire events at each stage of their lifecycle. You can hook into these events to run code before or after a model is saved, deleted, and so on. Events ending in -ing fire before the change is persisted. Events ending in -ed fire after.
Mass updates and mass deletes (User::where(...)->update(...)) do not fire saving, saved, updating, updated, deleting, or deleted events because the models are never retrieved from the database.

Closure-based event listeners

For simple cases, register closures inside the model’s booted method.
To run a closure on a queue instead, wrap it with the queueable helper.

The $dispatchesEvents property

When you want model events to feed into Laravel’s event system, map them to dedicated event classes using $dispatchesEvents.
The mapped event class receives the model instance in its constructor.

Creating an Observer class

When a model triggers several events, an Observer class is cleaner than scattered closures.
1

Generate the Observer with Artisan

Use make:observer with the --model option to get stubs for the relevant event methods.
This creates app/Observers/UserObserver.php.
2

Implement the event methods

Each method name corresponds to an event. The method receives the model instance.
3

Register the Observer

There are two ways to register an observer. The #[ObservedBy] attribute is preferred in Laravel 13.Option 1: #[ObservedBy] attribute (recommended)Add the attribute to the model class. No changes to AppServiceProvider are needed.
To attach multiple observers, repeat the attribute or pass an array.
Option 2: Register in AppServiceProvider
The #[ObservedBy] attribute lives in the Illuminate\Database\Eloquent\Attributes namespace. It is a native PHP 8.0+ attribute and the approach encouraged in Laravel 13.

Running Observers after a transaction commits

When a model is created or updated inside a database transaction, you may want the observer to run only after the transaction commits successfully. Implement ShouldHandleEventsAfterCommit to get this behavior.
If the event fires outside a transaction, it runs immediately as normal.

Temporarily disabling events

withoutEvents — silence all events for a block of code

Inside the closure passed to withoutEvents(), no model events fire.

saveQuietly — save without firing events

Similar quiet methods exist for other operations.

Practical use cases

Clearing cache automatically

Recording an audit log

getDirty() returns the attributes that have changed since the model was last synced.
The updating event fires before the record is saved to the database, so getDirty() contains the pending changes. Calling getDirty() inside an updated listener will return an empty array because the model has already been synced.

Next steps

Eloquent scopes

Learn how local and global scopes let you reuse query constraints across your application.
Last modified on March 28, 2026