$ lexprog.com

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

[October 11, 2024] Laravel

Laravel Collections: Beyond the Basics

Laravel Collections: Tips & Tricks

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

Laravel Collections: Tips & Tricks

Tip: Use tap() for Debugging

$users->tap(fn($c) => dump($c->count()))
    ->filter(fn($u) => $u->active)
    ->map(fn($u) => $u->name);

Inspect the collection mid-chain without breaking the flow.

Gotcha: map() Preserves Keys

collect([1 => 'a', 2 => 'b'])->map(fn($v) => strtoupper($v));
// [1 => 'A', 2 => 'B'] — keys preserved

Use values() to reset keys: ->map(...)->values().

Tip: when() for Conditional Operations

$collection->when($condition, fn($c) => $c->filter(...));

Cleaner than if statements in chains.

Tip: pipe() for Custom Transformations

$result = $collection->pipe(function ($items) {
    return $items->sum('price') / $items->count();
});

Gotcha: each() vs map()

each() is for side effects (returns original collection). map() transforms (returns new collection).

Tip: Lazy Collections for Large Datasets

User::cursor()->lazy()->filter(fn($u) => $u->active)->each(fn($u) => process($u));

Processes one record at a time, no memory issues.

Tip: groupBy() with Multiple Keys

$users->groupBy(['department', 'role']);
// Nested grouping: department -> role -> users

Gotcha: contains() Uses Loose Comparison

collect([1, 2, 3])->contains('1'); // true (loose)
collect([1, 2, 3])->containsStrict('1'); // false

Tip: reduce() for Accumulation

$total = $orders->reduce(fn($carry, $order) => $carry + $order->total, 0);

Tip: crossJoin() for Combinations

collect(['A', 'B'])->crossJoin(['1', '2']);
// [['A', '1'], ['A', '2'], ['B', '1'], ['B', '2']]

Tip: Use route:cache Carefully

php artisan route:cache is fast, but it doesn't work with closure-based routes. Every time you cache routes, Laravel serializes them. If you have Route::redirect() or closure callbacks, the cache breaks. Stick to controller-based routes in production.

Tip: Model APP_KEY Rotation

Rotating APP_KEY invalidates all encrypted data — cookies, encrypted DB columns, and password reset tokens. If you must rotate (e.g., after a leak), plan a migration that re-encrypts existing data with the new key.

Gotcha: Local Scope Leaks

Global scopes defined in booted() apply to ALL queries on that model — including relationships. An innocent User::all() in admin panel might exclude soft-deleted users if a global scope is active.

Senior Insight

Laravel collections are elegant, but they hide performance costs that can bite you at scale. I once profiled an API endpoint that used filter()->map()->values() on a 50K-element collection — it looked clean but took 2.3 seconds. The database query could have done the same work in 20ms. My rule: collections are for presentation logic (reformatting, sorting display data). For data filtering and aggregation, push the work to the database.

Source: Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/laravel), Spatie Blog (https://spatie.be/blog)

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