Files
jouwdriedee/.agents/skills/laravel-best-practices/rules/collections.md
T
2026-10-31 14:23:53 +01:00

2.4 KiB

Collection best practices

Use higher-order messages for simple operations

Explicit closure:

$users->each(function (User $user) {
    $user->markAsVip();
});

Concise equivalent:

$users->each->markAsVip();

Higher-order messages work with supported collection methods such as each, map, filter, and sum. Use an explicit closure when arguments or nontrivial logic would be clearer.

Choose between cursor() and lazy()

cursor() executes one query and hydrates one model at a time, but it cannot eager load relationships. The database driver's result buffering can still use a lot of memory for very large results. Use it for low-memory, attribute-only iteration when one long-running query is acceptable.

lazy() executes multiple chunked queries and returns a flat LazyCollection. It supports eager loading relationships for each chunk and avoids holding one database cursor open for the entire iteration.

With relationships:

User::with('roles')->lazy()->each(function (User $user) {
    // The roles for this chunk have been eager loaded.
});

Without relationships:

User::cursor()->each(function (User $user) {
    // Process model attributes.
});

Use lazyById() when updating records while iterating

lazy() uses offset pagination, so updating a column that affects the query can shift rows and make the iteration skip records or process them twice. lazyById() paginates by a monotonic key and is safer when you update other columns during iteration. Do not change the pagination key itself while iterating.

Use toQuery() for bulk operations on collections

Use toQuery() to build a query from the models in an Eloquent collection instead of manually constructing a whereIn clause.

Manual query:

User::whereIn('id', $users->modelKeys())->update(['active' => false]);

Collection query:

$users->toQuery()->update(['active' => false]);

toQuery() requires a non-empty Eloquent collection whose models are of the same type. Like other bulk Eloquent updates, it does not dispatch per-model update events, so use it only when you don't need those events.

Use #[CollectedBy] for custom collection classes

The #[CollectedBy] attribute declares the custom collection class without requiring a newCollection() override.

#[CollectedBy(UserCollection::class)]
class User extends Model {}