Use Laravel Collection Like a Pro

Mohasin Hossain
4 min readFeb 26, 2024

Never use a foreach loop outside of a collection! Let’s discover the beatify of Laravel Collection. Let’s dive into it.

Before we start just make sure you know about Imperative Programming vs Declarative Programming and Higher Order Functions.

Also I’m assuming that you know about some PHP array function like filter, map, each, reject & reduce. If not you may have a look and those are pretty easy.

The Golden Rule of Collection Programming

Never use a foreach loop outside of a collection! Every time you use a foreach loop, you’re doing it to accomplish something else, and I promise you that “something else” already has a name.

Need to loop over an array to perform some operation on each item and stuff the result into another array? You don’t need to loop, you need to map.

Need to loop over an array to strip out the items that don’t match some criteria? You don’t need to loop, you need to filter.

Pipeline programming is about operating at a higher level of abstraction. Instead of doing things with the items in a collection, you do things to the collection itself.

Map it, filter it, reduce it, sum it, zip it, reverse it, transpose it, flatten it, group it, count it, chunk it, sort it, slice it, search it; if you can do it with a foreach loop, you can do it with a collection method.

As soon as you elevate arrays from primitive types to objects that can have their own behavior, there’s no reason to ever use a foreach loop outside of the collection itself, and I’m going to prove it to you.

From this point forward, you won’t see a single foreach anywhere other than encapsulated inside a collection method. Let the games begin!

Now let’s refactor some usual methods with Laravel collection so that you can know how you can refactor your code in real project.

function getUserEmails($users)
{
$emails = [];

for ($i = 0; $i < count($users); $i++) {
$user = $users[$i];

if ($user->email !== null) {
$emails[] = $user->email;
}

}

return $emails;
}

Now you see how it turned into an elegant code after using power of Laravel collection. And once again no loop!

function getUserEmails($users)
{
return (collect($users))->filter(function ($user) {
return $user->email !== null;
})->map(function ($user) {
return $user->email;
})->toArray();
}

Another example

// Declarative approch
function binaryToDecimal($binary)
{
$total = 0;
$exponent = strlen($binary) - 1;

for ($i = 0; $i < strlen($binary); $i++) {
$decimal = $binary[$i] * (2 ** $exponent);
$total += $decimal;
$exponent--;
}

return $total;
}

// Collection pipeline
function binaryToDecimal($binary)
{
return collect(str_split($binary))
->reverse()
->values()
->map(function ($column, $exponent) {
return $column * (2 ** $exponent);
})->sum();
}

Last but not least example of collection pipeline. where you may use loop.

$limitOverDonors = Donor::active()->get()
->filter(function ($donor) {
return $donor->total_donation >= Enum::MAX_DONATION_LIMIT_PER_YEAR;
})->map(function($donor) {
return [
'donor_id' => $donor->id,
'total_donation' => $donor->total_donation,
];
});

Bonus:
Let’s see some collection method we use daily and how we get most of it with using callback/closure.

contains()

$names = collect(['Taylor', 'Jeffrey Way',  'Hasin Hayder', 'Shoag Hasan']);
$names->contains('Taylor');

// => true

Also we can pass closure to contains as parameter.

$names = collect(['Taylor', 'Jeffrey Way',  'Hasin Hayder', 'Shoag Hasan']);
$names->contains(function ($i, $name) {
return strlen($name) > 8;
});

// => true

first()

$names = collect(['Taylor', 'Jeffrey Way',  'Hasin Hayder', 'Shoag Hasan']);
$names->first();

// => 'Taylor'

Also we can pass closure to contains as parameter.

$names = collect(['Taylor', 'Jeffrey Way',  'Hasin Hayder', 'Shoag Hasan']);
$names->first(function ($i, $name) {
return $name[0] == 'J';
});

// => 'Jeffrey Way'

zip(): zip lets you take one collection, and pair every element in that collection with the corresponding element in another collection.

collect([1, 2, 3])->zip(['a', 'b', 'c']);

// => [
// [1, 'a'],
// [2, 'b'],
// [3, 'c'],
// ];

toAssoc(): Sometimes in some situation you may have array of two items and you need them as key value then toAssoc will save your life :)

$emailLookup = collect([
['john@example.com', 'John'],
['jane@example.com', 'Jane'],
['dave@example.com', 'Dave'],
])->toAssoc();

// => [
// 'john@example.com' => 'John',
// 'jane@example.com' => 'Jane',
// 'dave@example.com' => 'Dave',
// ]

groupBy(): groupBy() takes parameter as closure

$names = collect(['Taylor', 'Jeffrey Way',  'Hasin Hayder', 'Shoag Hasan', 'Mohsin']);
$names->groupBy(function ($name) {
return strlen($name);
});

//[
// 6=> ['Taylor', 'Mohsin'],
// 11=> ['Jeffrey Way', 'Shoag Hasan'],
// 12=> ['Hasin Hayder'],
//]

There are many collection method that takes parameter as closure/callback so just have a look all the collection methods and use them as your need.

Hope you found this helpful!

--

--

Mohasin Hossain

Senior Software Engineer | Mentor @ADPList | Backend focused | PHP, JavaScript, Laravel, Vue.js, Nuxt.js, MySQL, TDD, CI/CD, Docker, Linux