Understanding Laravel's High Order Collections

Posted on

A Brief Explanation

As of 5.4 Laravel comes with High Order collections, a short, more convenient way to filter through a collection.

Lets say i have a collection of Users, I want to loop through all of them and display the users full name ( first_name last_name ). Considering I have the method

class User extends Eloquent {

    public function fullName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

The old way to achieve my desired out put would be to….

$users = User::all(); // get all users
// The old way
$allUsersFullNames = $users->each(function(User $user) {

   return $user->fullName(); // Do something with the users full name
});

// do something with $allUsersFullNames

However with the High Order collections this can now be done as

$roles = Role::all(); // get all roles to test

$allUsersFullNames = $users->each->fullName();

How does this work?

Within the collections class we have an array of properties

protected static $proxies = [

  'contains', 'each', 'every', 'filter', 'first', 'map',

  'partition', 'reject', 'sortBy', 'sortByDesc', 'sum',

];

The Collections class does not contain the functions map() or contains() so when we call when we use ->map() or ->container() we are actually using the PHP magic method call() see [here]: https://www.johnmackenzie.co.uk/post/php-call-magic-method which looks like this;

public function __get($key)
{
    if (! in_array($key, static::$proxies)) {
          throw new Exception("Property [{$key}] does not exist on this collection instance.")
    }
    return new HigherOrderCollectionProxy($this, $key);
}

If the function name does not exist within the proxy array an error is returned, if not then a HighOrderProxy class if returned, it is in this HighOrderProxy class where all these new cool shorthand methods live.

Now we need to map the method full name() method found in the user class to the HighOrderProxy class, for this we use the PHP magic method call()

public function __call($method, $parameters)
{
     return $this->collection->{$this->method}(function ($value) use ($method, $parameters) {

          return $value->{$method}(...$parameters);
  });
}