Laravel default orderBy

Viewed 72519

Is there a clean way to enable certain models to be ordered by a property by default? It could work by extending the laravel's QueryBuilder, but to do so, you'll have to rewire some of it's core features - bad practice.

reason

The main point of doing this is - one of my models get's heavily reused by many others and right now you have to resort the order over and over again. Even when using a closure for this - you still have to call it. It would be much better to be able to apply a default sorting, so everyone who uses this model, and does not provide custom sorting options, will receive records sorted by the default option. Using a repository is not an option here, because it get's eager loaded.

SOLUTION

Extending the base model:

protected $orderBy;
protected $orderDirection = 'ASC';

public function scopeOrdered($query)
{
    if ($this->orderBy)
    {
        return $query->orderBy($this->orderBy, $this->orderDirection);
    }

    return $query;
}

public function scopeGetOrdered($query)
{
    return $this->scopeOrdered($query)->get();
}

In your model:

protected $orderBy = 'property';
protected $orderDirection = 'DESC';

// ordering eager loaded relation
public function anotherModel()
{
    return $this->belongsToMany('SomeModel', 'some_table')->ordered();
}

In your controller:

MyModel::with('anotherModel')->getOrdered();
// or
MyModel::with('anotherModel')->ordered()->first();
8 Answers

In Laravel 5.7, you can now simply use addGlobalScope inside the model's boot function:

use Illuminate\Database\Eloquent\Builder;

protected static function boot()
{
    parent::boot();

    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('created_at', 'desc');
    });
}

In the above example, I order the model by created_at desc to get the most recent records first. You can change that to fit your needs.

A note from my experience, never to use orderBy and GroupBy such term on global scope. Otherwise you will easily face database errors while fetching related models in other places.

Error may be something like:

"ORDER BY "created_at" is ambiguous"

In such case the solution can be giving table name before column names in your query scope.

"ORDER BY posts.created_at"

Thanks.

I built a mini Laravel package that can add default orderBy in your Eloquent model.

Using the DefaultOrderBy trait of this package, you can set the default column you want to orderBy.

use Stephenjude/DefaultModelSorting/Traits/DefaultOrderBy;

class Article extends Model
{
    use DefaultOrderBy;

    protected static $orderByColumn = 'title';
}

You can also set the default orderBy direction by setting the $orderByColumnDirection property.

protected static $orderByColumnDirection = 'desc';
Related