Reminder: Eloquent Observers Are Not Fired For Mass-Update or Mass-Delete

Eloquent Observer executed

If you have Observer events on updated/deleted rows, it's important to know that they are fired only when you update individual rows, and not when doing mass-update or mass-delete.

The code of the Observer:

app/Observers/PostObserver.php:

1class PostObserver
2{
3 public function deleted(Post $post)
4 {
5 // For example, delete the related image files
6 }
7}

This Observer is registered in the Service Provider:

app/Providers/AppServiceProvider.php:

1use App\Models\Post;
2use App\Observers\PostObserver;
3 
4class AppServiceProvider extends ServiceProvider
5{
6 public function boot()
7 {
8 Post::observe(PostObserver::class);
9 }
10}

And then imagine three different Eloquent queries:

1$post = Post::first();
2$post->delete();
3// This WILL execute the observer
4 
5Post::find(2)->delete();
6// This also WILL execute the observer
7 
8Post::where('id', '>', 3)->delete();
9// But this will NOT execute the observer!
10 
11$user->posts()->delete();
12// This will also NOT execute the observer!

The reason is that those Observer events come from Eloquent Model. In the case of delete() from Query Builder, it executes the query directly to the database, bypassing the individual model and its events.

Even if the result of the Query is one single Eloquent model, the observers will not be fired.

1Post::where('id', 4)->delete();

This is true for any Observer method, like updated() or updating(), delete() or deleting().


It is also important in the case of some packages that automatically register the Observers.

Like, in the case of Spatie Media Library, it registers the deleting() method which will not delete the associated Media files, if you are mass-deleting the related Model rows and not deleting a single Model.

laravel-medialibrary/src/InteractsWithMedia.php:

1trait InteractsWithMedia
2{
3 public static function bootInteractsWithMedia()
4 {
5 static::deleting(function (HasMedia $model) {
6 if ($model->shouldDeletePreservingMedia()) {
7 return;
8 }
9 
10 if (in_array(SoftDeletes::class, class_uses_recursive($model))) {
11 if (! $model->forceDeleting) {
12 return;
13 }
14 }
15 
16 $model->media()->cursor()->each(fn (Media $media) => $media->delete());
17 });
18 }

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month

Recent Premium Tutorials