Skip to main content
Tutorial Free

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

January 13, 2023
2 min read

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:

class PostObserver
{
public function deleted(Post $post)
{
// For example, delete the related image files
}
}

This Observer is registered in the Service Provider:

app/Providers/AppServiceProvider.php:

use App\Models\Post;
use App\Observers\PostObserver;
 
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Post::observe(PostObserver::class);
}
}

And then imagine three different Eloquent queries:

$post = Post::first();
$post->delete();
// This WILL execute the observer
 
Post::find(2)->delete();
// This also WILL execute the observer
 
Post::where('id', '>', 3)->delete();
// But this will NOT execute the observer!
 
$user->posts()->delete();
// 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.

Post::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:

trait InteractsWithMedia
{
public static function bootInteractsWithMedia()
{
static::deleting(function (HasMedia $model) {
if ($model->shouldDeletePreservingMedia()) {
return;
}
 
if (in_array(SoftDeletes::class, class_uses_recursive($model))) {
if (! $model->forceDeleting) {
return;
}
}
 
$model->media()->cursor()->each(fn (Media $media) => $media->delete());
});
}

Enjoyed This Tutorial?

Get access to all premium tutorials, video and text courses, and exclusive Laravel resources. Join our community of 10,000+ developers.

Comments & Discussion

No comments yet…

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.