Grayscale profile picture

Patrique Ouimet

Developer

Sortable Models with a Trait and Laravel's Query Scope

Sat, Sep 16, 2017 5:06 PM

Introduction

This post aims to provide a simple example of how to use traits with Laravel Query Scopes. The trait will sort columns based on "sort" and "direction" being found within the request, while protecting the database of unwanted sorting by adding a "sortables" property to the model. The end result looks like this with a Post model as an example:

Post::sort(request())

Sortable trait

Create the trait below in the "App" namespace (or change the namespace to suit your projects convention).

<?php

namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

trait Sortable
{
    /**
     * Scope a query to sort results.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param \Illuminate\Http\Request $request
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeSort(Builder $query, Request $request)
    {
        // Get sortable column
        $sortables = data_get($this, 'sortables', []);

        // Get the column to sort
        $sort = $request->get('sort');

        // Get the direction of which to sort
        $direction = $request->get('direction', 'desc');

        // Ensure column to sort is part of model's sortables property
        // and that the direction is a valid value
        if ($sort
            && in_array($sort, $sortables)
            && $direction
            && in_array($direction, ['asc', 'desc'])) {
            // Return ordered query
            return $query->orderBy($sort, $direction);
        }

        // No sorting, return query
        return $query;
    }
}

Model setup

The model setup requires 2 things:

  1. Add a use Sortable; statement (and import the class use App\Sortable as seen in the example below)
  2. Add a sortable public property which is an array of values that may be sorted

Example below allows sorting for the columns: id, views, published_at

<?php

namespace App;

use App\Sortable;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use Sortable;

    public $sortables = ['id', 'views', 'published_at'];
}

Trait usage

Below is an example of the usage of the sortable trait (query scope).

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        return Post::sort(request())->get();
    }
}

NOTE: This also works with pagination Post::sort(request())->paginate()

Extra

If you want to see this in action, I've created a small Laravel application to demo it.

Sortable Demo

Demo setup steps are as follows:

  1. Clone repository git clone https://github.com/patoui/sortable-demo.git
  2. Composer install composer install
  3. Run migrations php artisan migrate
  4. Run seeders php artisan db:seed
  5. Run artisan serv php artisan serve
  6. Visit 127.0.0.1:8000 (or value displayed from artisan serve)

Conclusion

That's it! I believe it's a pretty straight forward approach and made easy by Laravel's Query Scopes. Query Scopes are something I had avoided in my early Laravel development days, but I've learnt to embrace the simplicity that Laravel brings to complex situations and it's been enlightening. I suggest to everyone who's using Laravel to dig deep into the framework, there's TONS of hidden gems in there to make your life easier. Thank you Taylor Otwell and all the contributors for making my development life easier.

Hope you liked my article, please leave comments and share if you like!

Resources

Gist

Comments

Nov 19, 2017

This is a very nice trait I must say :)