Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
发布时间 - 2025-12-31 00:00:00 点击率:次用hasMany+belongsTo实现无限分类本质是自关联,Category模型需定义children(hasMany)和parent(belongsTo+withDefault)两个关系,配合withChildren递归加载或path路径字段优化查询性能。
用 hasMany + belongsTo 实现父子关联结构
无限分类本质是「自关联」,Laravel 中最直接的方式是让模型同时定义子集关系和父级关系。假设模型叫 Category,数据库有 id、name、parent_id(允许为 NULL)三个关键字段。
在 Category 模型里写两个关联方法:
public function children()
{
return $this->hasMany(Category::class, 'parent_id');
}
public function parent()
{
return $this->belongsTo(Category::class, 'parent_id')->withDefault();
注意:withDefault() 能避免根节点调用 $category->parent->name 时抛出 Trying to get property 'name' of non-object 错误。
-
children()用于向下查子类,支持链式调用如$category->children()->with('children') -
parent()用于向上查父类,建议加withDefault()防空指针 - 不要在迁移中给
parent_id加外键约束(除非你确定所有数据都严格符合树结构),否则软删除或临时断开关系时容易报错
递归获取完整树状结构(Eloquent 原生方式)
不依赖第三方包,用 Eloquent 的 with + 递归加载即可一次性取出整棵树。适用于层级不深(一般 ≤ 5 层)、数据量不大(几百条以内)的场景。
例如获取全部根节点及其子孙:
$tree = Category::whereNull('parent_id')
->with('children.children.children') // 手动展开 3 层
->get();
但硬编码层数不灵活。更通用的做法是封装一个递归加载器:
public function scopeWithChildren($query) { return $query->with(['children' => fn ($q) => $q->withChildren()]); }
然后调用:
$tree = Category::whereNull('parent_id')->withChildren()->get();
- 该写法会触发 N+1 查询优化后的嵌套查询,Laravel 9+ 支持这种闭包式嵌套加载
- 深度过大会导致内存占用高,比如单棵树有上千节点,建议改用「预计算路径」方案
- 如果用了
SoftDeletes,记得在children()关联里加withTrashed()或过滤逻辑,否则被软删的中间节点会导致树断裂
输出 HTML 树状菜单时避免重复渲染与深度失控
Blade 中递归渲染树最容易出问题的是:模板自己调自己导致无限循环,或没控制层级导致页面卡死。
推荐做法是在控制器中把数据转成扁平化带层级标识的数组,再传给视图;或者在 Blade 中用带深度参数的子视图。
例如建一个 resources/views/categories/tree.blade.php:
@props(['categories', 'depth' => 0])@foreach($categories as $category)
{{ $category->name }} @if($category->children->isNotEmpty()) @include('categories.tree', ['categories' => $category->children, 'depth' => $depth + 1]) @endif @endforeach
在主视图中调用:
@include('categories.tree', ['categories' => $tree])
- 必须传入
$depth并做递归限制(比如加@if($depth 判断),防止意外形成环形引用崩溃 - 不要在循环里反复调用
$category->children,必须提前用with加载好,否则变 N+1 查询 - 若需生成
下拉菜单,建议用collect($tree)->toTree()(需手动实现)或转成带前缀的扁平数组,比递归渲染更可控
需要高性能或复杂操作时,考虑「路径字段」替代递归查询
当分类数超千、层级深、频繁查询祖先/后代时,Eloquent 递归很快成为瓶颈。这时应放弃纯关系模型,改用「路径存储」方案:在表中加一个 path 字段(如 "0001/0005/0023"),用字符串前缀匹配快速定位上下文。
典型操作示例:
- 查某节点的所有后代:
where('path', 'like', '0001/0005/%') - 查某节点的所有祖先:
whereRaw("path REGEXP '^([0-9]+/)*0005$')"(或拆分后 in 查询) - 移动节点只需更新其自身及所有后代的
path字段,无需递归更新关系
缺点是写操作变重、路径长度需预估、迁移成本高。但读多写少的后台管理场景下,这是最稳的选择。
真正麻烦的不是怎么实现,而是前期没想清楚:这个分类会不会被前端频繁拖拽排序?会不会导出到其他系统?要不要支持多语言别名?这些需求一旦出现,光靠 parent_id 就撑不住了。
# php
# laravel
# html
# 前端
# go
# 编码
# 多语言
# 拖拽排序
# 内存占用
# Object
# NULL
# if
# 封装
# 父类
# 子类
# select
# 字符串
# 递归
# 循环
# 指针
# Property
# 闭包
# 空指针
# regexp
# 数据库
# 加载
# 会不会
# 树状
# 链式
# 里加
# 转成
# 棵树
# 的是
# 这是
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】
实例解析Array和String方法
零基础网站服务器架设实战:轻量应用与域名解析配置指南
Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践
Angular 表单中正确绑定输入值以确保提交与验证正常工作
详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)
Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理
如何撰写建站申请书?关键要点有哪些?
Laravel如何实现API版本控制_Laravel API版本化路由设计策略
如何在建站之星网店版论坛获取技术支持?
iOS验证手机号的正则表达式
php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】
详解Android——蓝牙技术 带你实现终端间数据传输
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
Laravel怎么在Blade中安全地输出原始HTML内容
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
绝密ChatGPT指令:手把手教你生成HR无法拒绝的求职信
Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】
googleplay官方入口在哪里_Google Play官方商店快速入口指南
JavaScript如何实现路由_前端路由原理是什么
Laravel Fortify是什么,和Jetstream有什么关系
PythonWeb开发入门教程_Flask快速构建Web应用
Laravel如何实现本地化和多语言支持?(i18n教程)
今日头条微视频如何找选题 今日头条微视频找选题技巧【指南】
Java Adapter 适配器模式(类适配器,对象适配器)优缺点对比
如何在Tomcat中配置并部署网站项目?
Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧
Laravel模型关联查询教程_Laravel Eloquent一对多关联写法
js实现点击每个li节点,都弹出其文本值及修改
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
如何用5美元大硬盘VPS安全高效搭建个人网站?
清除minerd进程的简单方法
如何在云主机上快速搭建网站?
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】
如何在阿里云通过域名搭建网站?
JavaScript数据类型有哪些_如何准确判断一个变量的类型
米侠浏览器网页背景异常怎么办 米侠显示修复
如何快速生成专业多端适配建站电话?
网站制作价目表怎么做,珍爱网婚介费用多少?
Laravel如何使用Scope本地作用域_Laravel模型常用查询逻辑封装技巧【手册】
中山网站制作网页,中山新生登记系统登记流程?
如何在云服务器上快速搭建个人网站?
laravel怎么实现图片的压缩和裁剪_laravel图片压缩与裁剪方法
详解阿里云nginx服务器多站点的配置
如何快速完成中国万网建站详细流程?
如何用y主机助手快速搭建网站?
青岛网站建设如何选择本地服务器?
Midjourney怎样加参数调细节_Midjourney参数调整技巧【指南】


copeWithChildren($query)
{
return $query->with(['children' => fn ($q) => $q->withChildren()]);
}