
6 minutes read
15 Laravel Collections tips to refactor your codebase
Table of contents
- → Introduction to Laravel Collections
-
→
Laravel Collection methods I always use
- → Create a collection from an array
- → Transform a collection back into an array
- → Use collections in foreach loops
- → Prefer each() over foreach when you want fluency
- → Merge collections correctly
- → Use filter() to clean up
- → Use sum(), avg(), min(), and max() for numbers
- → Use higher order messages for concise iteration
- → Keep or drop keys with only() and except()
- → Use dump() and dd() on collections
- → Pick random items with random()
- → Replace temporary arrays with map()
- → Use isEmpty() and isNotEmpty() for readability
- → Use when() and unless() to conditionally transform a collection
- → Extend collections with your own methods
- → Conclusion
Introduction to Laravel Collections
Laravel Collections are a powerful tool for working with arrays. They wrap native PHP functions, add helpful helpers, and give you a fluent API.
In general, collections behave immutably: most methods return a new collection instead of changing the current one. Some methods do mutate the instance (for example, transform
, push
, pop
, shift
, put
, prepend
). When you want a non-mutating alternative, reach for map
. See the docs for transform
and map
.
Laravel Collection methods I always use
Create a collection from an array
To create a collection from an array, use the collect()
helper.
$collection = collect(); // Empty collection. $collection = collect([1, 2, 3]); // Collection from an array.
You can also use the static constructor Collection::make(...)
from Illuminate\Support\Collection
:
use Illuminate\Support\Collection; $collection = Collection::make([1, 2, 3]); // Static constructor.
Transform a collection back into an array
To turn a collection into an array, call toArray()
.
$collection = collect([1, 2, 3]); $array = $collection->toArray();
This isn’t necessary most of the time, but it can come in handy.
Use collections in foreach loops
Collections work in foreach
just like arrays.
foreach ($collection as $item) { // ... }
You can also access the key:
foreach ($collection as $key => $value) { // ... }
They are iterable because Collection
implements PHP’s IteratorAggregate
.
Prefer each() over foreach when you want fluency
The most basic collection use is replacing a foreach
with each()
.
$collection = collect(['Foo', 'Bar', 'Baz']); $collection->each(function ($value, $key) { // ... });
Returning false
from the callback stops the iteration early. See each
.
Merge collections correctly
Merging two collections is simple with merge()
.
$numbers = collect([10, 20])->merge(collect([30])); // [10, 20, 30] $settings = collect(['theme' => 'light'])->merge(collect(['theme' => 'dark'])); // ['theme' => 'dark'] (string keys overwrite)
Behavior note: merge
appends numeric-keyed values and overwrites on duplicate string keys. See merge
.
Use filter() to clean up
It’s common to build a temporary array inside a loop. Collections let you skip that with filter()
:
return $bar->filter(function ($baz) { return $baz->something(); });
filter()
preserves the original keys. If order or JSON output matters, chain ->values()
to reset the indices:
return $bar->filter(fn ($baz) => $baz->something()) ->values();
Use sum(), avg(), min(), and max() for numbers
Collections include handy math helpers:
$numbers = collect([1, 2, 3, 4, 5]); $numbers->sum(); // 15 $numbers->avg(); // 3 (average() is an alias) $numbers->min(); // 1 $numbers->max(); // 5
Use higher order messages for concise iteration
Higher order messages (HOM) make chains shorter by calling a method on each item.
foreach (User::where('foo', 'bar')->get() as $user) { $user->notify(new SomeNotification); }
Refactor with each()
:
User::where('foo', 'bar') ->get() ->each(function (User $user) { $user->notify(new SomeNotification); });
Then use HOM to drop the callback:
User::where('foo', 'bar') ->get() ->each ->notify(new SomeNotification);
Keep or drop keys with only() and except()
Need specific keys?
$original = [ 'foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz', ]; $new = collect($original)->only(['foo', 'bar']);
Need to remove some?
$new = collect($original)->except(['foo', 'bar']);
Use dump() and dd() on collections
You can call these right on the collection:
// Before: dd($collection->where('foo', 'bar')); // After: $collection->where('foo', 'bar')->dd();
dd()
dumps and halts; dump()
dumps and lets code continue. See dd
and dump
.
Pick random items with random()
$one = collect(['Foo', 'Bar', 'Baz'])->random(); // Returns a single item. $two = collect(['a', 'b', 'c'])->random(2); // Returns a Collection of 2 items.
Requesting more items than exist throws InvalidArgumentException
. See random
.
Replace temporary arrays with map()
This is a common pattern:
namespace App\Twitter; use App\Twitter\Tweet; use Illuminate\Support\Facades\Http; class Client { public function tweets() { $tweets = Http::get('https://api.twitter.com/2/users/me') ->json('tweets'); $tmp = []; foreach ($tweets as $tweet) { $tmp[] = new Tweet(...$tweet); } return $tmp; } }
With collections, you can skip the temporary variable:
namespace App\Twitter; use App\Twitter\Tweet; use Illuminate\Support\Facades\Http; class Client { public function tweets() { return Http::get('https://api.twitter.com/2/users/me') ->collect('tweets') // Response::collect exists on HTTP client responses ->map(fn (array $value) => new Tweet(...$value)) ->all(); // Keep returning an array } }
When to use: prefer map()
when you want a new collection; transform()
mutates the existing one.
See the HTTP client’s collect
on responses.
Use isEmpty() and isNotEmpty() for readability
if ($collection->isEmpty()) { // Do something if the collection is empty. }
if ($collection->isNotEmpty()) { // Do something if the collection is not empty. }
Use when() and unless() to conditionally transform a collection
Collections use the Illuminate\Support\Traits\Conditionable
trait, which adds when()
and unless()
for fluent conditional logic.
$models = Model::query()->when( $something, fn ($query) => $query->where('something', true), fn ($query) => $query->where('something_else', true), )->get();
This trait also powers other parts of the framework (for example, Eloquent factories, Logger, HTTP PendingRequest
, and Carbon), so you’ll see the same pattern elsewhere. See when
/ unless
on collections.
Extend collections with your own methods
Along with other Laravel classes, collections are “macroable,” so you can extend them at runtime. Register macros in a service provider’s boot
method.
app/Providers/AppServiceProvider.php:
use Illuminate\Support\Str; use Illuminate\Support\Collection; class AppServiceProvider extends ServiceProvider { public function boot() { Collection::macro('pluralize', function () { return $this->map(function ($value) { return Str::plural($value); }); }); } }
Now your custom method is available anywhere:
$collection = collect(['apple', 'banana', 'strawberry']); $collection->pluralize(); // ['apples', 'bananas', 'strawberries']
Conclusion
- Prefer non-mutating methods like
map()
; know whentransform()
changes the instance. each()
can replaceforeach
and stops early when the callback returnsfalse
.only()
/except()
,isEmpty()
/isNotEmpty()
, andwhen()
/unless()
make intent clear.- Use higher order methods (
->each->notify(...)
) for clean, readable chains. - Remember gotchas:
filter()
preserves keys;merge()
overwrites string keys and appends numeric keys.
Next steps: explore Eloquent collections and lazy collections to work efficiently with large datasets.
Did you like this article? Then, keep learning:
- Learn how to clear different caches in Laravel to avoid common issues
- Master error handling in Laravel's HTTP client for better debugging
- Deepen understanding of Laravel versioning and upgrade processes
- Improve code readability and maintainability with Laravel architecture tips
- Discover more Laravel best practices to improve your codebase
- Understand Laravel's Conditionable trait for cleaner conditional logic
- Explore Laravel's query builder to write efficient where clauses
- Learn how to secure your Laravel app with essential security tips
0 comments