Laravel默认订单

M K*_*M K 36 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();
}
Run Code Online (Sandbox Code Playgroud)

在你的模型中:

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

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

在你的控制器中:

MyModel::with('anotherModel')->getOrdered();
// or
MyModel::with('anotherModel')->ordered()->first();
Run Code Online (Sandbox Code Playgroud)

Jer*_*ten 45

在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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}
Run Code Online (Sandbox Code Playgroud)

现在模型默认排序.请注意,如果您还在查询中手动定义订单: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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}
Run Code Online (Sandbox Code Playgroud)

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

MyModel::withoutGlobalScope(OrderScope::class)->get();
Run Code Online (Sandbox Code Playgroud)

无额外范围类的解决方案

如果您不希望为范围创建一个完整的类,则可以(因为Laravel 5.2)也在模型的boot()方法中定义内联的全局范围:

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

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

MyModel::withoutGlobalScope('order')->get();
Run Code Online (Sandbox Code Playgroud)


Jos*_*our 22

另一种方法是通过覆盖newQuery模型类中的方法.这只有在您从未希望结果由另一个字段排序时才有效(因为->orderBy()稍后添加另一个字段不会删除此默认字段).所以这可能不是你通常想要做的,但是如果你需要总是按某种方式排序,那么这将有效:

protected $orderBy;
protected $orderDirection = 'asc';

/**
 * Get a new query builder for the model's table.
 *
 * @param bool $ordered
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function newQuery($ordered = true)
{
    $query = parent::newQuery();    

    if (empty($ordered)) {
        return $query;
    }    

    return $query->orderBy($this->orderBy, $this->orderDirection);
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ker 15

是的,您需要扩展Eloquent以始终将此作为任何查询的标准.在需要订购时,在查询中添加order by语句有什么问题?这是最干净的方式,也就是说,你不需要"解开"Eloquent以通过自然顺序获得结果.

MyModel::orderBy('created_at', 'asc')->get();
Run Code Online (Sandbox Code Playgroud)

除此之外,最接近您想要的是在模型中创建查询范围.

public function scopeOrdered($query)
{
    return $query->orderBy('created_at', 'asc')->get();
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以调用ordered方法而不是get检索已排序的结果.

$data = MyModel::where('foo', '=', 'bar')->ordered();
Run Code Online (Sandbox Code Playgroud)

如果你想在不同的模型中使用它,你可以创建一个基类,并将它扩展到你想要访问这个作用域方法的模型.

  • 这不是默认情况 (7认同)

iva*_*zer 14

你应该使用可以应用于所有查询的雄辩的全局范围(你也可以为它设置参数).

对于关系,你可以使用这个有用的技巧:

class Category extends Model {
    public function posts(){
        return $this->hasMany('App\Models\Post')->orderBy('title');
    }
}
Run Code Online (Sandbox Code Playgroud)

order by当我们从一个类别中获取它们时,这将添加到所有帖子.如果您order by在查询中添加一个,则此默认值order by将取消!


小智 11

在Laravel 5.7中,您现在可以addGlobalScope在模型的启动功能内简单使用:

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

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

在上面的示例中,我对模型进行排序created_at desc以首先获取最新记录。您可以更改它以满足您的需求。


Ema*_*mad 9

Joshua Jabbour 给出的略有改进的答案

您可以使用他在 Trait 中提供的代码,然后将该特征添加到您希望订购它们的模型中。

<?php

namespace App\Traits;

trait AppOrdered {
    protected $orderBy = 'created_at';
    protected $orderDirection = 'desc';

    public function newQuery($ordered = true)
    {
        $query = parent::newQuery();

        if (empty($ordered)) {
            return $query;
        }

        return $query->orderBy($this->orderBy, $this->orderDirection);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在您想要订购数据的任何模型中,您都可以使用

class PostsModel extends Model {

    use AppOrdered;
    ....
Run Code Online (Sandbox Code Playgroud)

现在每次您请求该模型时,都会对数据进行排序,这在某种程度上更有条理,但我的答案是 Jabbour 的答案。