Pushing Logic to Custom Collections

This is a technique that I recently found useful. Eloquent models allow you to specify a custom collection object to be returned – which sometimes can be a great place to put some business logic.

While refactoring WhichBeach, I used a custom collection to move some logic from the Beach model to a custom BeachCollection. This allowed me to test the logic in isolation.

Creating the Custom Collection

I created a new class extending Laravel’s Eloquent Collection:

<?php

namespace WhichBeach\Beach\Support;

use Illuminate\Database\Eloquent\Collection;

class BeachCollection extends Collection
{

}

In my model, I instructed Laravel to use my custom collection:

<?php

namespace WhichBeach\Beach\Entities;

use WhichBeach\BaseModel;
use WhichBeach\Beach\Support\BeachCollection;

class Beach extends BaseModel
{
    /**
     * Create a new Eloquent Collection instance.
     *
     * @param  array  $models
     * @return \WhichBeach\Beach\Support\BeachCollection
     */
    public function newCollection(array $models = [])
    {
        return new BeachCollection($models);
    }
}

Adding the Logic

Now that I had a place to put my custom code, I could add any number of helper methods to be used throughout my application:

/**
 * Filter the beaches to only contain major beaches
 *
 * @return static
 */
public function major()
{
    return $this->where('is_major', true);
}

/**
 * Filter the beaches to only the ones that have the lowest score
 *
 * @return static
 */
protected function lowestScoring()
{
    return $this->where('score', $this->min('score'));
}

Edit: The above code block has been improved after an excellent suggestion by Joseph Silber in the comments.

This has given me the ability to write more fluent code when dealing with beaches:

$topBeach = $beaches->major()->lowestScoring()->random();

Being able to write the line above is a big plus for me – the less cognitive load required to understand the intended output, the better.

Let me know of any good examples of using custom collections in the comments!