php Laravel 默认 orderBy

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20701216/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 03:26:26  来源:igfitidea点击:

Laravel default orderBy

phplaravel

提问by M K

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.

默认情况下,是否有一种干净的方法可以让某些模型按属性进行排序?它可以通过扩展 Laravel 的 QueryBuilder 来工作,但要这样做,您必须重新连接它的一些核心功能 - 不好的做法。

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

采纳答案by David Barker

Yes you would need to extend Eloquent to always do this as standard for any query. What's wrong with adding an order by statement to the query when you need it ordered? That is the cleanest way, ie, you dont need to 'unhack' Eloquent to get results by natural order.

是的,您需要扩展 Eloquent 以始终​​将其作为任何查询的标准。在需要订购时向查询添加 order by 语句有什么问题?这是最干净的方法,即,您不需要“破解”Eloquent 即可按自然顺序获得结果。

MyModel::orderBy('created_at', 'asc')->get();

Other than that the closest thing to what you want would be to create query scopes in your models.

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

public function scopeOrdered($query)
{
    return $query->orderBy('created_at', 'asc')->get();
}

You can then call orderedas a method instead of getto retrieve your ordered results.

然后,您可以将其ordered作为方法调用,而不是get检索您订购的结果。

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

If you wanted this across different models you could create a base class and just extend it to the models you want to have access to this scoped method.

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

回答by Jeroen Noten

Before Laravel 5.2

Laravel 5.2 之前

Nowadays we can solve this problem also with global scopes, introduced in Laravel 4.2 (correct me if I'm wrong). We can define a scope class like this:

现在我们也可以用 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;
        }
    }
}

Then, in your model, you can add the scope in the boot()method:

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

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

Now the model is ordered by default. Note that if you define the order also manually in the query: MyModel::orderBy('some_column'), then it will only addit as a secondary ordering (used when values of the first ordering are the same), and it will not override. To make it possible to use another ordering manually, I added an (optional) macro (see above), and then you can do: MyModel::unordered()->orderBy('some_column')->get().

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

Laravel 5.2 and up

Laravel 5.2 及更高版本

Laravel 5.2 introduced a much cleaner way to work with global scopes. Now, the only thing we have to write is the following:

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

Then, in your model, you can add the scope in the boot()method:

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

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

To remove the global scope, simply use:

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

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

Solution without extra scope class

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

If you don't like to have a whole class for the scope, you can (since Laravel 5.2) also define the global scope inline, in your model's boot()method:

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

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

You can remove this global scope using this:

您可以使用以下方法删除此全局范围:

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

回答by Joshua Jabbour

Another way of doing this could be by overriding the newQuerymethod in your model class. This only works if you never, ever want results to be ordered by another field (since adding another ->orderBy()later won't remove this default one). So this is probably not what you'd normally want to do, but if you have a requirement to always sort a certain way, then this will work:

另一种方法是覆盖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);
}

回答by Jonathan Roy

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

在 Laravel 5.7 中,您现在可以简单地addGlobalScope在模型的 boot 函数中使用:

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 descto get the most recent records first. You can change that to fit your needs.

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

回答by ivahidmontazer

you should use eloquent global scopethat can apply to all queries(also you can set parameter for it).

您应该使用可应用于所有查询的eloquent全局范围(您也可以为其设置参数)。

And for relations you can use this useful trick:

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

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

this will add order byto all posts when we get them from a category. If you add an order byto your query, this default order bywill cancel!

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

回答by Emad Ha

An slightly improved answer given by Joshua Jabbour

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

you can use the code he offered in a Trait, and then add that trait to the models where you want them to be ordered.

您可以使用他在 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);
    }

}

then in whichever model you want the data to be ordered you can use use:

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

class PostsModel extends Model {

    use AppOrdered;
    ....

now everytime you request that model, data will be ordered, that's somehow more organized, but my answers is Jabbour's answer.

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

回答by Hasnat Babur

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

根据我的经验,从不使用orderByGroupBy全局范围内的此类术语。否则在其他地方获取相关模型时很容易遇到数据库错误。

Error may be something like:

错误可能类似于:

"ORDER BY "created_at" is ambiguous"

“ORDER BY “created_at”不明确”

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

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

"ORDER BY posts.created_at"

Thanks.

谢谢。