$ lexprog.com

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

[March 09, 2026] Eloquent ORM

Eloquent Mass Update: Avoiding Loops

Eloquent Mass Update: Avoiding Loops

────────────────────────────────────────────────────────

Eloquent Mass Update: Avoiding Loops

Tip: Query Builder Update

Post::where('category_id', 1)->update(['status' => 'archived']);

Single query, no model loading.

Gotcha: No Events Fired

Mass update doesn't fire updating or updated events.

Tip: upsert() for Bulk Operations

Post::upsert([
    ['slug' => 'a', 'title' => 'A'],
    ['slug' => 'b', 'title' => 'B'],
], uniqueBy: ['slug'], update: ['title']);

Gotcha: updated_at Not Auto-Updated

Mass update doesn't touch updated_at. Add it manually:

Post::where('active', true)->update(['updated_at' => now()]);

Tip: Chunk Update

Post::where('status', 'draft')->chunkById(100, function ($posts) {
    foreach ($posts as $post) {
        $post->update(['status' => 'published']);
    }
});

Fires events for each model.

Gotcha: update() on Collection

$posts->each->update(['status' => 'archived']);

Loads all models and fires events. Slow for large datasets.

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

Bulk updates in Eloquent seem simple — just run Model::where(...)->update([...]) — but they bypass model events, accessors, and booted traits. I've seen teams use bulk updates to set published = 1 on a thousand posts, then wonder why the cache invalidation observer didn't fire. For any batch operation that needs event handling, chunk the updates and process them through individual model instances.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

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