Sort by array

This method for Laravel Eloquent is used to sort items by array keys, which can be useful when you want to run by a number of possible scenarios and assign priority based on the array's occurance.

Laravel3 / 3Level:

In order for this to work, you need a couple of things. In fact, you may very well learn two things through this tutorial, instead of just one. First, we need to tell Laravel that we are using a custom collection class. We accomplish this the following way:

use App\Collections\CustomCollection;
use Illuminate\Database\Eloquent\Model;

class Post extends Model {

    // ...

    /**
     * Create a new Eloquent Collection instance.
     *
     * @param  array  $models
     * @return Illuminate\Database\Eloquent\Collection
     */
    public function newCollection(array $models = [])
    {
        return new CustomCollection($models);
    }
}

Now each time you use Eloquent to retrieve data from your database, your new custom collection class will be used. Now that we got this working, we only need to create a new file called /App/Collections/CustomCollection.php.

When you've done so, simply have that class extend Laravel's default Eloquent Collection class, as follows:

use Illuminate\Database\Eloquent\Collection;

class CustomCollection extends Collection
{
    // here we can add stuff to Laravel's default collection class 
}

In my case, I've added a method to allow for sorting by an array, allowing for the following new Eloquent method:

$posts = Post::query()
    ->where('published', true)
    ->where('tags', 'LIKE', '%Laravel%')
    ->get()
    // the sortByArray() below allows sorting by occurance
    ->sortByArray(fn ($row) => [
        // prioritize items based on the following order
        $row->featured == true, // featured articles
        $row->visits > 9000, // popular articles
        $row->author == 'author', // our best author author
        $row->created < now()->subMonths(6), // recent articles
    ]);

This allows us to essentially create our own 'algorithm' of sorts that inspects each post and assigns a relevance based on the matches index, ranging from index 0 (most relevant) all the way to 3 (least relevant). This could be useful for determining what is relevant for, let's say, a social feed.

P.s. In case you're wondering: the query() method is provided by Laravel itself for more expressive syntax. It's the same as calling where() directly.

You're probably wondering how sortByArray() works under the hood. There you go:

use Illuminate\Database\Eloquent\Collection;

class CustomCollection extends Collection
{
    /**
     * Sort the collection by array occurance.
     * 
     * @source https://marknuyens.nl/tips/sort-by-array
     *
     * @param  callable  $source  The source function that returns the array.
     * @return self
     */
    public function sortByArray(callable $source)
    {
        return $this->sortBy(function ($item) use ($source) {
            if ($checks = $source($item)) {
                if (is_array($checks)) {
                    $checks = array_values($checks);
                    foreach ($checks as $order => $apply) {
                        if ($apply) {
                            return $order;
                        }
                    }
                }
            }
        });
    }
}

Feel fee to copy it into your own custom Eloquent collection, I wouldn't mind—in fact, I would be honoured. 😉

Conclusion

Although this may prove to be somewhat extensive for a simple function, the basis allows for further extension of the Eloquent Collections, per model. If you would want to use these kind of methods throughout your entire Laravel application, you can do so via Laravel's Macro functions. But I found this method to be more versatile.