Eloquent Without Events: Bulk Operations
Eloquent Without Events: Bulk Operations
Eloquent Without Events: Bulk Operations
Tip: withoutEvents() for Bulk Updates
User::withoutEvents(function () {
User::query()->update(['status' => 'active']);
});
Skips all model events during the operation.
Gotcha: Query Builder Never Fires Events
User::where('active', false)->delete();
No deleting or deleted events fire. This is always the case with query builder.
Tip: withoutTouching() for Performance
Comment::withoutTouching(function () {
Comment::where('post_id', $postId)->update(['approved' => true]);
});
Prevents parent timestamp updates.
Gotcha: Observers Still Register
withoutEvents() doesn't unregister observers — it just prevents them from firing during the callback.
Tip: tap() for Inline Modifications
$post->tap(function ($p) {
$p->title = 'New Title';
$p->save();
});
Gotcha: update() on Collection
$users->each->update(['status' => 'active']);
This fires events for EACH model. Use query builder update() for bulk.
Tip: Use cursor() for Memory-Neutral Iteration
When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.
Tip: whereHas() vs load() — Two Different Things
whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.
Gotcha: withCount() Adds a Subquery
withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.
Senior Insight
The withoutEvents() method is a performance lifesaver for bulk operations, but it's also a way to accidentally skip critical business logic. I've seen data migration scripts use withoutEvents() and bypass validation, corrupting data in the process. My rule: never use withoutEvents() in production code paths — only in maintenance scripts and tests. And when you do use it, log the operation explicitly so you can trace what was skipped.
Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)