You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 9中如何对嵌套中间表数据执行orderBy排序?

问题描述

需要按以下顺序对数据进行排序:

  1. Group的ends_at字段
  2. Group的name字段
  3. Subgroup的name字段(若存在子组)
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 14:45:02