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 replaceforeachand 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