我的员工和组表之间存在多对多关系。我已经创建了数据透视表,并且一切正常。但是,我的员工表上有一个 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 值进行最后排序的技巧是什么?
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');
}
只需在字段中添加一个减号并将顺序更改为 DESC。
$q->orderBy(\DB::raw('-`sortOrder`'), 'desc');
在 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');
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 值,则使用实际值。
我最近使用 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');
您还可以这样做,而不是依赖于任意大的数字:
public function employees()
{
return $this
->hasMany('Employee')
->select(['*', DB::raw('sortOrder IS NULL AS sortOrderNull')])
->orderBy('sortOrderNull')
->orderBy('sortOrder');
}
它还有一个额外的好处,即受 SQLite 支持。
您可以更优雅地执行以下操作,以获得更好的效果
这将从最新到最旧排序,从空到最后排序。
->orderByRaw("CASE WHEN column_to_order IS NULL THEN 0 ELSE 1 END DESC")
->orderBy('column_to_order', 'DESC')
这将首先排列空记录,然后从最旧到最新。
->orderByRaw("CASE WHEN column_to_order IS NULL THEN 0 ELSE 1 END ASC")
->orderBy('column_to_order', 'ASC')
PostgreSQL 的解决方法
对于数字类型:
DB::table('t')
->select(['id', 'val'])
->orderBy(DB::raw("coalesce(val, 0)"), 'desc')
对于文本类型:
orderBy(DB::raw("coalesce(val, '')"), 'desc')
技巧是将排序列中的
NULL
值替换为零(或空字符串),以便可以将其作为普通整数(或文本)值进行排序。
对于嵌套关系
我没有找到在嵌套关系中进行相同操作的解决方案。不得不即兴创作,这就是我想出的:
// 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 调用中。
如果其他人需要对 Laravel 集合使用 null 最后排序可以使用这个技巧,
collect([
['order' => null],
['order' => 1],
['order' => 44],
['order' => 4],
['order' => null],
['order' => 2],
])
->sortBy('order')
->sortBy(fn($item, $key) => $item['order'] == null);
此代码将对空值进行最后排序:
$posts = Post::orderByRaw('ISNULL(`rank`), `rank` ASC')->get();
->orderBy('sortOrder', 'is', 'null')->orderBy('sortOrder', 'asc')
似乎有效。