Laravel 9中如何对嵌套中间表数据执行orderBy排序?
问题描述
需要按以下顺序对数据进行排序:
- Group的
ends_at字段 - Group的
name字段 - Subgroup的
name字段(若存在子组) - Group与User中间表的
role字段
目前对实现方式毫无头绪,当前在blade文件中写了查询代码,不确定是否应该将逻辑移至控制器,使用的是Laravel 9版本。相关代码如下:
模型代码
User.php
public function groups(): BelongsToMany { return $this->belongsToMany(Group::class) ->using(GroupUserPivot::class) ->withPivot( 'id', 'role', 'subgroup_id', ); }
Group.php
public function subgroups(): HasMany { return $this->hasMany(Subgroup::class) ->orderBy('name'); }
GroupUserPivot.php
public function group(): BelongsTo { return $this->belongsTo(Group::class); } public function subgroup(): BelongsTo { return $this->belongsTo(Subgroup::class); }
Blade视图代码
groups.blade.php
@foreach ($user->groups()->latest('ends_at')->orderBy('name')->orderByPivot('role')->get() as $userGroup) <h1>{{ $userGroup->name }}</h1> @if ($userGroup->pivot->subgroup) {{ $userGroup->pivot->subgroup->name }} @endif @endforeach
解决方案
1. 把查询逻辑移到控制器(更规范)
Blade视图该专心做渲染,查询逻辑丢去控制器或者模型里才符合Laravel的MVC思路,后续维护和复用也方便,别在blade里写查询了,移出去。
2. 实现你要的多条件排序
要按子组名称排序,得关联上subgroups表才行,因为子组可能不存在,用leftJoin不会漏掉没子组的记录。
控制器里这么写查询
// 控制器方法里 $user = User::findOrFail($userId); $sortedGroups = $user->groups() ->leftJoin('subgroups', 'group_user.subgroup_id', '=', 'subgroups.id') ->orderBy('groups.ends_at', 'desc') // 要是想正序排就把desc改成asc ->orderBy('groups.name') ->orderBy('subgroups.name') // 没子组的会因为name是NULL排在前面 ->orderBy('group_user.role') ->select('groups.*', 'group_user.*') // 确保把需要的字段都选出来 ->get(); return view('groups', compact('user', 'sortedGroups'));
然后修改blade视图
@foreach ($sortedGroups as $userGroup) <h1>{{ $userGroup->name }}</h1> @if ($userGroup->pivot->subgroup) {{ $userGroup->pivot->subgroup->name }} @endif @endforeach
要是这个排序逻辑常用,封装到模型里更方便
在User模型加个方法:
// User.php public function sortedGroups() { return $this->groups() ->leftJoin('subgroups', 'group_user.subgroup_id', '=', 'subgroups.id') ->orderBy('groups.ends_at', 'desc') ->orderBy('groups.name') ->orderBy('subgroups.name') ->orderBy('group_user.role') ->select('groups.*', 'group_user.*'); }
控制器里直接调用:
$sortedGroups = $user->sortedGroups()->get();
补充说明
- 用
leftJoin是为了保留没有子组的记录,排序时NULL值默认会排在前面,要是想让有子组的先排,可以改成orderByRaw('subgroups.name IS NULL, subgroups.name')。 - 排序顺序完全按照你要求的4个条件来,一步不差。
- 把逻辑移出blade,代码结构更清晰,以后改排序规则也不用去视图里找。
内容的提问来源于stack exchange,提问作者iDev247
相关产品推荐
相关产品推荐

