php - Laravel 默认 orderBy

标签 php laravel

有没有一种简洁的方法可以让某些模型按默认的属性排序? 它可以通过扩展 laravel 的 QueryBuilder 来工作,但要这样做,你必须重新连接它的一些核心功能 - 不好的做法。

原因

这样做的主要目的是 - 我的一个模型被许多其他人大量重用,现在你必须一遍又一遍地重新排序。即使为此使用闭包 - 你仍然必须调用它。能够应用默认排序会更好,因此使用此模型且不提供自定义排序选项的每个人都将收到按默认选项排序的记录。在这里使用存储库不是一个选项,因为它会被预先加载。

解决方案

扩展基础模型:

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();
}

在您的模型中:

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

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

在您的 Controller 中:

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

最佳答案

在 Laravel 5.2 之前

现在我们也可以使用 Laravel 4.2 中引入的全局作用域来解决这个问题(如果我错了,请纠正我)。我们可以像这样定义一个作用域类:

<?php namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ScopeInterface;

class OrderScope implements ScopeInterface {

    private $column;

    private $direction;

    public function __construct($column, $direction = 'asc')
    {
        $this->column = $column;
        $this->direction = $direction;
    }

    public function apply(Builder $builder, Model $model)
    {
        $builder->orderBy($this->column, $this->direction);

        // optional macro to undo the global scope
        $builder->macro('unordered', function (Builder $builder) {
            $this->remove($builder, $builder->getModel());
            return $builder;
        });
    }

    public function remove(Builder $builder, Model $model)
    {
        $query = $builder->getQuery();
        $query->orders = collect($query->orders)->reject(function ($order) {
            return $order['column'] == $this->column && $order['direction'] == $this->direction;
        })->values()->all();
        if (count($query->orders) == 0) {
            $query->orders = null;
        }
    }
}

然后,在您的模型中,您可以在 boot() 方法中添加范围:

protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}

现在模型是默认排序的。请注意,如果您在查询中也手动定义了顺序:MyModel::orderBy('some_column'),那么它只会添加它作为二级排序(用于第一个排序的值相同),并且不会覆盖。为了使手动使用另一个排序成为可能,我添加了一个(可选)宏(见上文),然后你可以这样做:MyModel::unordered()->orderBy('some_column')->get() .

Laravel 5.2 及更高版本

Laravel 5.2 引入了一种更简洁的方式来处理全局范围。现在,我们唯一需要写的是:

<?php namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class OrderScope implements Scope
{

    private $column;

    private $direction;

    public function __construct($column, $direction = 'asc')
    {
        $this->column = $column;
        $this->direction = $direction;
    }

    public function apply(Builder $builder, Model $model)
    {
        $builder->orderBy($this->column, $this->direction);
    }
}

然后,在您的模型中,您可以在 boot() 方法中添加范围:

protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}

要删除全局范围,只需使用:

MyModel::withoutGlobalScope(OrderScope::class)->get();

没有额外作用域类的解决方案

如果你不喜欢为作用域设置一个完整的类,你也可以(从 Laravel 5.2 开始)在你的模型的 boot() 方法中定义全局作用域:

protected static function boot() {
    parent::boot();
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('date', 'desc');
    });
}

您可以使用以下命令删除此全局范围:

MyModel::withoutGlobalScope('order')->get();

关于php - Laravel 默认 orderBy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20701216/

相关文章:

php - Laravel ajax 404 未找到错误

PHP - Laravel - 直接从 Eloquent 获取平均值,而不必循环结果

php - 删除通过mysql_query获得的特定行结果?

php - 确定数据中的连胜

php - joomla 1.7 : overriding pagination. php 不工作

php - 在 Laravel/PHP 中将额外的字段插入数组

javascript - 如何使用 DataTables (serverSide : true)?

php - 使用 Laravel Collection 获取重复值

php - 添加自定义字段 jQuery 数据表

php - 我想在报告评论后保持在同一页面上