Php Laravel默认排序依据

Php Laravel默认排序依据,php,laravel,Php,Laravel,是否有一种干净的方法可以使某些型号在默认情况下由属性订购? 它可以通过扩展laravel的QueryBuilder来工作,但要做到这一点,您必须重新连接它的一些核心功能——糟糕的做法 原因 这样做的要点是——我的一个模型被许多其他人大量重用,现在你不得不一次又一次地求助于订单。即使使用闭包,您仍然必须调用它。最好能够应用默认排序,这样使用此模型且不提供自定义排序选项的每个人都将收到按默认选项排序的记录。在这里,使用存储库不是一个选项,因为它需要立即加载 解决方案 扩展基本模型: protecte

是否有一种干净的方法可以使某些型号在默认情况下由属性订购? 它可以通过扩展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();
}
在控制器中:

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

是的,您需要扩展Eloquent,以便始终将其作为任何查询的标准。当需要排序时,将ORDERBY语句添加到查询中有什么错?这是最干净的方法,也就是说,你不需要用雄辩的口才来按自然顺序获得结果

MyModel::orderBy('created_at', 'asc')->get();
除此之外,最接近您想要的是在模型中创建查询范围

public function scopeOrdered($query)
{
    return $query->orderBy('created_at', 'asc')->get();
}
然后,您可以调用
ordered
作为方法,而不是
get
来检索已排序的结果

$data = MyModel::where('foo', '=', 'bar')->ordered();

如果需要跨不同的模型执行此操作,您可以创建一个基类,并将其扩展到希望访问此作用域方法的模型。

另一种方法是重写模型类中的
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);
}
拉威尔5.2之前 现在,我们也可以用全局范围来解决这个问题,这是在Laravel4.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;
        }
    }
}
现在,默认情况下,模型已排序。请注意,如果您也在查询中手动定义订单:
MyModel::orderBy('some_column')
,则它将仅将其添加为二级订单(当第一个订单的值相同时使用),并且不会覆盖。为了能够手动使用另一个排序,我添加了一个(可选)宏(见上文),然后您可以执行:
MyModel::unordered()->orderBy('some_column')->get()

拉威尔5.2及以上 Laravel5.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);
    }
}
要删除全局范围,只需使用:

MyModel::withoutGlobalScope(OrderScope::class)->get();
没有额外作用域类的解决方案

如果您不想为作用域定义一个完整的类,那么(因为Laravel 5.2)也可以在模型的
boot()
方法中内联定义全局作用域:

protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}
protected static function boot() {
    parent::boot();
    static::addGlobalScope(new OrderScope('date', 'desc'));
}
protected static function boot() {
    parent::boot();
    static::addGlobalScope('order', function (Builder $builder) {
        $builder->orderBy('date', 'desc');
    });
}
您可以使用以下方法删除此全局作用域:

MyModel::withoutGlobalScope('order')->get();
您应该使用可应用于所有查询的eloquent(也可以为其设置参数)

对于关系,您可以使用以下有用的技巧:

class Category extends Model {
    public function posts(){
        return $this->hasMany('App\Models\Post')->orderBy('title');
    }
}
当我们从一个类别中获得所有帖子时,这将向所有帖子添加
orderby

如果您在查询中添加一个
orderby
,此默认
orderby
将被取消

Joshua Jabbour给出的答案稍有改进

您可以使用他在Trait中提供的代码,然后将该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);

}

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

在Laravel 5.7中,您现在只需在模型的启动功能中使用
addGlobalScope

use Illuminate\Database\Eloquent\Builder;

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

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

在上面的示例中,我通过在desc处创建的
对模型进行排序,以首先获取最新的记录。根据我的经验,您可以更改它以满足您的需要。

在全局范围内,绝对不要使用
orderBy
GroupBy
这样的术语。否则,在其他地方获取相关模型时,您将很容易遇到数据库错误

错误可能类似于:

“创建时间”的订单不明确

在这种情况下,解决方案可以在查询范围的列名之前给出表名

"ORDER BY posts.created_at"
谢谢。

我构建了一个迷你版,可以在您雄辩的模型中添加默认orderBy

使用此软件包的
DefaultOrderBy
trait,您可以设置要排序的默认列

使用Stephenjude/DefaultModelSorting/Traits/DefaultOrderBy;
类文章扩展了模型
{
使用DefaultOrderBy;
受保护的静态$orderByColumn='title';
}
您还可以通过设置
$orderByColumnDirection
属性来设置默认的orderBy方向

protected static$orderByColumnDirection='desc';

这不是默认情况阅读答案。。。我建议你在默认情况下不要这样做。我在我的问题中写道“即使使用闭包,你仍然必须调用它。”是的,你是这样做的,我在第一行说。。。你得为这件事干好口才。。。正如你写的那样:坏习惯。我已经给了你最接近你想要的解决方案。。。对不起,我不能再多说了。它不能回答问题。在默认情况下对记录进行排序有时非常有用,特别是当您的结果按
weight
列或类似方式排序时。如果没有
orderBy(),您就不能期望任何特定的顺序,这对于某些型号来说是很好的,但有时让它们始终按顺序排列会很好。如果Laravel对默认范围有一些特殊的方法,如果需要的话,还有一种方法可以退出,我会很高兴的。这也适用于
where
,当我们有一些用户通常无法访问的记录时。有点像软删除,但更灵活
"ORDER BY posts.created_at"