如何在Laravel Nova中按billno字段对数据进行分组
参考材料
- Laravel Nova索引(Index)列表页截图:

- 对应业务数据库结构截图:

Laravel Nova按billno字段分组展示关联数据实现方案
核心原则是优先用Nova原生扩展能力,不要直接改底层模板,避免版本升级出现兼容问题,按自身业务需求选对应方案即可。
方案一:轻量快速实现(适合明细列表直接分组、不需要复杂交互的场景)
直接在对应明细资源里调整查询逻辑和字段展示,三步即可完成:
- 重写资源的索引查询逻辑
打开对应Nova明细资源文件(一般存放在app/Nova/目录下),覆写indexQuery方法,提前按billno排序、预加载关联数据,注意保留原生搜索、过滤能力,不要直接覆盖原有逻辑:
public static function indexQuery(NovaRequest $request, $query) { return $query->with('billGoods') // 替换成自身模型里实际的关联关系名 ->orderBy('billno', 'desc') ->when($request->get('search'), function($q) use ($request) { // 保留原搜索能力,避免分组后搜索失效 $q->where('billno', 'like', "%{$request->get('search')}%") ->orWhereHas('billGoods', function($subQ) use ($request) { $subQ->where('goods_name', 'like', "%{$request->get('search')}%"); }); }); }
- 配置列表字段,合并展示同billno下的明细
用Stack字段把同组的明细内容嵌套在同一列,避免billno字段重复渲染:
public function fields(NovaRequest $request) { return [ Text::make('账单编号', 'billno')->sortable(), Stack::make('消费明细', function () { return $this->billGoods->map(fn($item) => Line::make("{$item->goods_name} | 数量:{$item->num} | 单价:{$item->price}") )->toArray(); })->onlyOnIndex(), Number::make('账单总金额', 'total_amount')->sortable(), DateTime::make('创建时间', 'created_at')->sortable(), ]; }
- 合并重复billno单元格
注册一个全局前端脚本,在索引页加载完成后遍历表格,把相邻同billno的单元格做rowspan合并即可,几十行原生JS就能实现,不需要安装第三方扩展包。
方案二:原生规范实现(适合需要展开/折叠、交互完整的场景)
如果要做可折叠的分组效果,不要在明细资源硬改,直接利用Nova原生的关联能力实现:
- 新建账单主表对应的Nova资源(比如
Bill.php),将billno作为主资源的唯一标识字段 - 在主资源里用
HasMany字段关联明细资源:
HasMany::make('账单明细', 'billGoods', BillDetail::class),
- 开启Nova原生行展开功能,点击对应billno行就会异步加载同组下的所有关联明细,交互、分页、搜索都是原生支持的,稳定性最高,后续升级Nova版本也不会出现兼容问题。
踩坑提醒:如果直接用SQL的
groupBy做分组,一定要重写分页器的总数统计逻辑,把同billno的多条明细算作1条计数,不然分页条数和实际展示的分组数会对不上。
内容的提问来源于stack exchange,提问作者Prox Grewal
相关产品推荐
相关产品推荐

