如何在 Laravel 中使用 Eloquent 对 NULL 值进行最后排序

问题描述 投票:0回答:12

我的员工和组表之间存在多对多关系。我已经创建了数据透视表,并且一切正常。但是,我的员工表上有一个 sortOrder 列,我用它来确定他们的显示顺序。 sortOrder 列中值为 1 的员工应排在第一位,值为 2 的员工应排在第二位,依此类推。 (如果按降序排序则向后)sortOrder 列是允许空值的整数列。

我已经设置了组模型来按排序列对员工进行排序,但我遇到了问题。空值始终首先显示。我尝试使用 ISNULL 和类似的 SQL 方法来代替常规的“asc”或“desc”,但只收到错误。

这是我的组模型中的代码:

class Group extends Eloquent {

public function employees()
    {
        return $this->belongsToMany("Employee")->orderBy('sortOrder', 'asc');
    }
}

这是我在控制器中用来访问我的模型的内容:

$board = Group::find(6)->employees;

Laravel 中对 NULL 值进行最后排序的技巧是什么?

php many-to-many laravel eloquent
12个回答
86
投票

Laravel 没有考虑

ISNULL
方法,但是,您可以将其作为原始查询传递,并且仍然使用它,因为它比
IF
语句更有效,并且如果您超出范围,结果将保持不变1000000 名员工(接受答案),就像这样:

public function employees()
{
    return $this->hasMany('Employee')
                ->orderBy(DB::raw('ISNULL(sortOrder), sortOrder'), 'ASC');
}

更新: 您还可以使用 orderByRaw() 方法:

public function employees()
{
    return $this->hasMany('Employee')
                ->orderByRaw('ISNULL(sortOrder), sortOrder ASC');
}

35
投票

只需在字段中添加一个减号并将顺序更改为 DESC。

$q->orderBy(\DB::raw('-`sortOrder`'), 'desc');

10
投票

在 Laravel 5.2 或更高版本中,只需调用

orderByRaw
。您甚至可以对聚合值而不是列进行排序。在以下示例中,如果没有子模型,
max_st
可以是
null

Model::where('act', '2')
    ->leftJoin('submodels', 'model.id', '=', 'submodels.model_id')
    ->select('models.*', DB::raw('MAX(submodels.st) as max_st')),
    ->orderByRaw('max_st DESC NULLS LAST');

6
投票
public function employees()
{
    return $this
        ->hasMany('Employee')
        ->select(['*', DB::raw('IF(`sortOrder` IS NOT NULL, `sortOrder`, 1000000) `sortOrder`')])
        ->orderBy('sortOrder', 'asc');
}

说明:
IF 语句解决了这里的问题。如果找到 NULL 值,则会将一些大数字分配给 sortOrder。如果发现不是 NULL 值,则使用实际值。


6
投票

我最近使用 Laravel 5.6 遇到了这个问题,junkystu 的答案对我来说非常完美。然而我们的测试框架使用 sqlite,因此测试不断返回 500 错误。

这是我们想出的方案,它应该与数据库驱动程序稍微无关。

上升

$query->orderBy(DB::raw('column_to_sort IS NULL, column_to_sort'), 'asc');

降序

$query->orderBy(DB::raw('column_to_sort IS NOT NULL, column_to_sort'), 'desc');

5
投票

您还可以这样做,而不是依赖于任意大的数字:

public function employees()
{
    return $this
        ->hasMany('Employee')
        ->select(['*', DB::raw('sortOrder IS NULL AS sortOrderNull')])
        ->orderBy('sortOrderNull')
        ->orderBy('sortOrder');
}

它还有一个额外的好处,即受 SQLite 支持。


1
投票

您可以更优雅地执行以下操作,以获得更好的效果

  1. 这将从最新到最旧排序,从空到最后排序。

     ->orderByRaw("CASE WHEN column_to_order IS NULL THEN 0 ELSE 1 END DESC")
     ->orderBy('column_to_order', 'DESC')
    
    
  2. 这将首先排列空记录,然后从最旧到最新。

   ->orderByRaw("CASE WHEN column_to_order IS NULL THEN 0 ELSE 1 END ASC")
   ->orderBy('column_to_order', 'ASC')

0
投票

PostgreSQL 的解决方法

对于数字类型:

DB::table('t')
    ->select(['id', 'val'])
    ->orderBy(DB::raw("coalesce(val, 0)"), 'desc')

对于文本类型:

orderBy(DB::raw("coalesce(val, '')"), 'desc')

技巧是将排序列中的

NULL
值替换为零(或空字符串),以便可以将其作为普通整数(或文本)值进行排序。


0
投票

对于嵌套关系

我没有找到在嵌套关系中进行相同操作的解决方案。不得不即兴创作,这就是我想出的:

// Sorting order
$order = 'asc'

// Order Products by $product->store->manager?->name
Product::orderByRaw(
    "(". 
        Manager::select('name')
            ->whereHas('store', function (Builder $q) {
                $q->whereColumn('id', 'products.store_id');
            })
            ->toSql() 
    .") $order NULLS LAST"
);

编辑
好吧,我发现上面的查询很慢,所以我这样重写了:

$query = Product::query()
    ->leftJoin('stores', 'stores.id', 'products.store_id')
    ->leftJoin('managers', 'managers.id', 'stores.manager_id')
    ->orderByRaw("managers.name $order NULLS LAST");

连接的运行速度比子查询快 3 倍。但请注意,您需要将表前缀(例如

'products.title'
)添加到对此
$query
的所有进一步 Eloquent 调用中。


0
投票

如果其他人需要对 Laravel 集合使用 null 最后排序可以使用这个技巧,

collect([
    ['order' => null],
    ['order' => 1],
    ['order' => 44],
    ['order' => 4],
    ['order' => null],
    ['order' => 2],
])
    ->sortBy('order')
    ->sortBy(fn($item, $key) => $item['order'] == null);


0
投票

此代码将对空值进行最后排序:

$posts = Post::orderByRaw('ISNULL(`rank`), `rank` ASC')->get();

-3
投票
->orderBy('sortOrder', 'is', 'null')->orderBy('sortOrder', 'asc')

似乎有效。

© www.soinside.com 2019 - 2024. All rights reserved.