$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[April 08, 2025] Eloquent ORM

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)

────────────────────────────────────────────────────────
<-- back to posts