4 min read

Laravel Collections: A Cleaner Way To Manipulate Arrays

Laravel Collections: A Cleaner Way To Manipulate Arrays
Image credit: https://www.e-spincorp.com/documentation/laravel-php-web-framework/

Laravel Collections are an integral part of Laravel's toolkit, providing a fluent and chainable way to work with arrays of data. Collections are an abstraction layer that extends the capabilities of native PHP arrays, allowing developers to perform complex operations in a more expressive and intuitive manner.

Seriously, Laravel Collections are the bomb!

Here are some of the key benefits of Laravel Collections:

  1. Fluent Syntax: Collections leverage PHP's powerful closures to enable a fluent and readable syntax. This allows you to chain methods together, leading to code that looks very neat and organized.
  2. Advanced Operations: Collections provide a plethora of methods for filtering, mapping, reducing, sorting, and aggregating data.  Say bye to millions of foreach loops in your codebase.
  3. Lazy Loading: Collections are "lazy", meaning they only perform the requested operation when needed. This lazy evaluation minimizes memory usage and improves performance, especially when dealing with large datasets.
  4. Highly Extensible: Laravel Collections are highly extensible, and developers can create custom methods to fit their specific needs. This flexibility encourages code reusability and maintainability.

It's time to get to the juicy part. Let's walk through some example code snippets to see the collection's magic in action.

Transform An Array Into A Collection

Using the collect helper, an array can be converted into a collection. Easy peasy!

// Empty collection
$collection = collect(); 

//collection from array
$array_converted_to_collection = collect([1,2,3,4,5]);

//Collection from the use \Illuminate\Support\Collection class
$collection_initialised_from_class = new Collection([1,2,4,6]);

Transform A Collection Back To An Array

You may need to convert a collection back to an array in some instances. I had to do something similar in a project sometime back. Pretty straightforward too.

//convert collection to array 
$collection = collect(["hello", "world"]);
$collection_back_to_array = $collection->toArray();

Collections Work With Foreach Loops But It's Beautiful To Use each instead

/**Looping through collections with foreach */
$fruits_collection = collect(["apples","bananas","oranges"]);

foreach($fruits_collection as $fruit){
	echo "We love {$fruit}";
}

//doing it the romantic way with ->each
$fruits_collection->each(function($value, $key){
 	echo $value;
 })

Need To Merge Collections Together?

You can also conveniently merge two collections together in perfect harmony.

$carbs_collection = collect(["Fried rice", "Jollof", "Bread"]);
$protein_collection = collect(["and Chicken","and Pork"]);

$meal_collection = $carbs_collection->take(1)->merge($protein_collection->take(1));
//outputs: Fried rice and Chicken

filter() it

Say you needed to loop through an array/collection and return all elements that meet a certain condition, you will typically do it like so:

$results = [];

foreach($records as $record){
    if($record->meetsACertainCriteria()){
    	$results [] = $record;
    }
}

return $results;

What if I told you that we can make this look cleaner? There:

return $records->filter(function($record){
	return $record->meetsACertainCriteria();
});

sum(), average(), min(), max() To The Rescue

Calculating the sum, avg or some aggregate statistics is a right of passage for every developer..lol.  Normally, with arrays, you will do something like so for calculating the sum for example:

$grades = ['2','4','9','10'];
$total_score = 0;

foreach($grades as $grade){
	$total_score+=$grade;
}

But hey, we are the cool-looking guys, right?

$cool_grades = collect($grades);
$cool_grades->sum();
$cool_grades->min();
$cool_grades->max();

Want To Write Even Shorter Code? How About Higher Order Messages?

Higher-order messages make your code look more concise. You can read more in the Laravel documentation. Let's walk through an example.

//What is usually done
foreach (User::where('foo', 'bar')->get() as $user) {
  $user->notify(new SomeNotification);
}

//refactoring to use collections
User::where('foo', 'bar')
->get()
->each(function (User $user) {
	$user->notify(new SomeNotification);
});

//and now even better with higher order messages
User::where('foo', 'bar')
->get()
->each
->notify(new SomeNotification);

I'm definitely in love 😍

Replace Lengthy unset() code With only() and except()

Let's say you had an associative array and you wanted to create a new array that only contains certain keys. This is how you'd normally do it with native PHP arrays:

$original= [
    "fruit" => "apple",
    "carbs" => "rice",
    "protein" => "chicken"
];

$keep = ["fruits", "carbs"];

$new = array_intersect_key($original, array_flip($keep));

/*
Output: [
    "fruit" => "apple",
    "carbs" => "rice"
];
*/

This definitely looks like a mess right? Let's do something cleaner with collections.

$new = collect($original)->only('fruit','carbs');

That's lit, right? 😊

Let's say we wanted to do the reverse. Exclude some of the key pairs in the original array.

$new = collect($original)->except('fruit','carbs');

Lottery Time With random()

$collection = collect(['Ghana','Nigeria','Rwanda']);
$random_country = $collection->random();

Kick Out Temporary Variables With map()

We have all done this at some point:

  1. Loop through collection
  2. Do some modifications and assign it to a temporary variable
  3. Return that variable

Like so:

namespace App\Cooking;

use App\Cooking\Meal;
use Illuminate\Support\Facades\Http;

class Client
{
    public function meals()
    {
        $ingredients = Http::get('https://api.kitchen.com/ingredients')
            ->json('ingredients');

        $tmp = [];

        foreach ($ingredients as $ingredient) {
            $tmp[] = new Meal($ingredient);
        }

        return $tmp;
    }
}

Let's clean this up with collections and eliminate the temporary variable

class Client
{
    public function meals()
    {
       return Http::get('https://api.kitchen.com/ingredients')
            ->collect('ingredients')
            ->map(fn ($ingredient) => new Meal($ingredient));

     
    }
}

Extend Collections With Your Own Methods

Collections are "macroable", which allows you to add additional methods to the Collection class at run time. The Illuminate\Support\Collection class' macro method accepts a closure that will be executed when your macro is called.

use Illuminate\Support\Str;
use Illuminate\Support\Collection;
 
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Collection::macro('capitalise', function () {
            return $this->map(function ($value) {
                 return Str::upper($value);
            });
        });
    }
}

Then in your codebase:

$collection = collect(['apple', 'banana', 'strawberry']);
 
//['APPLE', 'BANANA', 'STRAWBERRY');
$collection->capitalise();

Whoo! There is so much we can do with Laravel collections.

Hope you loved this article.