Skillshub laravel-collections

Laravel Collections

install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/riasvdv/skills/laravel-collections" ~/.claude/skills/comeonoliver-skillshub-laravel-collections && rm -rf "$T"
manifest: skills/riasvdv/skills/laravel-collections/SKILL.md
source content

Laravel Collections

Quick Decision Guide

"Which method do I use?"

Find items:

NeedMethod
First item matching condition
first(fn)
or
firstWhere('key', 'value')
First item or throw
firstOrFail(fn)
Exactly one match or throw
sole(fn)
Check value exists
contains('value')
or
contains('key', 'value')
Check key exists
has('key')
All items match condition
every(fn)

Transform items:

NeedMethod
Transform each item
map(fn)
Transform + flatten one level
flatMap(fn)
Extract one field
pluck('name')
Extract field, keyed by another
pluck('name', 'id')
Select multiple fields
select(['id', 'name'])
Change keys
mapWithKeys(fn)
or
keyBy('id')
Map into class instances
mapInto(DTO::class)

Filter items:

NeedMethod
Keep matching items
filter(fn)
Remove matching items
reject(fn)
Filter by field value
where('status', 'active')
Filter by field in list
whereIn('status', ['active', 'pending'])
Keep specific keys only
only(['id', 'name'])
Remove specific keys
except(['password'])
Remove duplicates
unique('email')

Group & organize:

NeedMethod
Group by field
groupBy('category')
Group with custom key+value
mapToGroups(fn)
Split pass/fail
partition(fn)
Key by field (1 item per key)
keyBy('id')
Split into chunks
chunk(100)

Aggregate:

NeedMethod
Sum values
sum('price')
Average
avg('score')
Min/Max
min('price')
/
max('price')
Reduce to single value
reduce(fn, $initial)
Count occurrences
countBy('type')
Percentage matching
percentage(fn)

Combine collections:

NeedMethod
Merge (overwrites string keys)
merge($other)
Append values (re-indexes)
concat($other)
Keep only shared values
intersect($other)
Get values not in other
diff($other)
Pair items by index
zip($other)

Mutability Rules

Immutable (return new collection) - almost everything:

map
,
filter
,
reject
,
sort
,
merge
,
pluck
,
unique
, ...

Mutating (modify in place, return

$this
) - memorize these:
transform
,
push
,
prepend
,
put
,
pull
,
pop
,
shift
,
splice
,
forget

// WRONG mental model:
$filtered = $collection->push('item'); // $collection IS modified

// Correct approach when you want immutability:
$new = $collection->concat(['item']); // $collection is unchanged

Common Pitfalls

1.
each()
vs
map()
vs
transform()

// each(): Side-effects, returns ORIGINAL collection
$users->each(fn($u) => $u->notify(new Welcome));

// map(): Transformation, returns NEW collection (original unchanged)
$names = $users->map(fn($u) => $u->name);

// transform(): Transformation, MUTATES original collection
$users->transform(fn($u) => $u->name); // $users is now a collection of names!

2.
pluck()
key overwrites

When using

pluck('name', 'id')
, duplicate keys silently overwrite:

collect([
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 1, 'name' => 'Bob'],   // Overwrites Alice
])->pluck('name', 'id');
// [1 => 'Bob']  -- Alice is lost!

Use

groupBy
if duplicates are expected.

3.
filter()
without callback removes all falsy values

collect([0, 1, '', 'hello', null, false, []])->filter()->all();
// [1, 'hello'] -- 0, '', null, false, [] are ALL removed

Use

whereNotNull()
if you only want to remove nulls.

4. N+1 queries with collection iteration

// BAD: N+1 queries
$users->each(fn($user) => $user->posts->count());

// GOOD: Eager load first
$users->load('posts');
$users->each(fn($user) => $user->posts->count());

5. Memory with large datasets

// BAD: Loads all users into memory
User::all()->each(fn($u) => process($u));

// GOOD: Constant memory with cursor
User::cursor()->each(fn($u) => process($u));

// GOOD: Chunked processing
User::chunk(1000, fn($users) => $users->each(fn($u) => process($u)));

6.
sort()
preserves keys

collect([3, 1, 2])->sort()->all();
// [1 => 1, 2 => 2, 0 => 3] -- keys preserved!

collect([3, 1, 2])->sort()->values()->all();
// [1, 2, 3] -- use values() to re-index

Higher-Order Messages

Proxy pattern for clean, concise collection operations:

// Instead of:
$users->map(function ($user) { return $user->name; });

// Write:
$users->map->name;

// Works with methods too:
$users->each->markAsVip();
$users->sum->votes;
$users->filter->isAdmin();
$users->sortBy->name;
$users->reject->isBlocked();
$users->groupBy->department;

Supported on:

average
,
avg
,
contains
,
each
,
every
,
filter
,
first
,
flatMap
,
groupBy
,
keyBy
,
map
,
max
,
min
,
partition
,
reject
,
skipUntil
,
skipWhile
,
some
,
sortBy
,
sortByDesc
,
sum
,
takeUntil
,
takeWhile
,
unique
.

Extending with Macros

Register custom collection methods (typically in a service provider's

boot
method):

use Illuminate\Support\Collection;

Collection::macro('toUpper', function () {
    return $this->map(fn($value) => strtoupper($value));
});

// Usage
collect(['foo', 'bar'])->toUpper(); // ['FOO', 'BAR']

Reference Files