PHP怎样获取栏目树形结构_PHP构栏目树方法【树形】
发布时间 - 2026-02-02 00:00:00 点击率:次最常用方式是查出全部栏目后用PHP组装树形结构,关键在于提前构建父子映射并用引用挂载子树,避免N+1查询和重复遍历,兼顾性能与可控性。
PHP递归查询数据库生成栏目树的常见写法
直接从数据库查出全部栏目再用PHP组装树形结构,是最常用也最可控的方式。关键不是“能不能”,而是“怎么组织数据才不掉坑”。
假设表结构为 id、name、parent_id,且根节点 parent_id = 0 或 NULL(需统一):
- 先用一次
SELECT * FROM category ORDER BY parent_id, sort ASC拿到全部数据,避免N+1查询 - 用
parent_id做键,把所有子节点归组到一个$map数组里:$map[$row['parent_id']][] = $row -
递归函数只负责拼装层级,
不查库;入口从
$map[0](或$map[NULL])开始
注意:如果 parent_id 允许为 NULL,别用 0 当作根判断条件,否则 isset($map[0]) 会误判。
用引用方式一次性构建树,避免重复遍历
递归调用本身没问题,但若每次都在全量数组里 array_filter 找子项,性能会随层级和数量陡增。更稳的做法是提前建好父子映射,再用引用“挂载”子树。
核心逻辑是:遍历原始数组时,对每个节点,把它塞进其父节点的 children 数组中;根节点直接进结果集。这要求原始数据已按 parent_id 排序,或至少保证父节点在子节点前被处理(可先按 id 升序查出,再用 usort 调整顺序)。
- 初始化空数组
$tree和$refs(用于存每个id对应的引用) - 循环每条记录:
$refs[$row['id']] = &$row,然后if ($row['parent_id'] && isset($refs[$row['parent_id']])) { $refs[$row['parent_id']]['children'][] = &$row; } - 最后遍历原始数组,把
parent_id为空的节点推入$tree
这种方式没有递归调用开销,也不依赖函数栈深度,适合几百个节点以内的栏目结构。
MySQL 8.0+ 用 WITH RECURSIVE 直接查出树形结果
如果数据库是 MySQL 8.0+ 或 PostgreSQL,能用 WITH RECURSIVE 一次性查出带层级和路径的扁平结果,PHP 层只需简单分组,甚至不用递归。
例如 MySQL 查询:
WITH RECURSIVE tree AS ( SELECT id, name, parent_id, 0 AS level, CAST(id AS CHAR) AS path FROM category WHERE parent_id = 0 UNION ALL SELECT c.id, c.name, c.parent_id, t.level + 1, CONCAT(t.path, '-', c.id) FROM category c INNER JOIN tree t ON c.parent_id = t.id ) SELECT * FROM tree ORDER BY path;
返回结果已按树序排列,PHP 只需按 level 缩进或用栈维护当前父级即可生成嵌套数组。缺点是无法在低版本 MySQL 使用,且复杂查询可能影响缓存效率。
容易被忽略的边界情况
真实业务里,栏目树常踩的不是语法坑,而是数据逻辑坑:
-
parent_id指向了不存在的id(孤儿节点),会导致某层“消失”或无限递归(若没加深度限制) - 存在循环引用,比如 A → B → C → A,纯 PHP 递归会爆栈,必须加
$visited集合或层级计数器截断 -
前端需要展开/折叠状态,但后端返回的是完整树,建议加
has_children字段(通过EXISTS子查询或预聚合),而不是让前端猜 -
多语言或站点隔离场景下,
category表常带site_id或lang字段,漏加 WHERE 条件会导致树错乱
树形结构看着简单,真正稳定运行靠的是对数据一致性的预判,而不是递归写得有多漂亮。
# mysql
# php
# 前端
# go
# 后端
# 栈
# 多语言
# 递归函数
# 排列
# NULL
# if
# sort
# select
# 递归
# 循环
# map
# postgresql
# 数据库
# 遍历
# 子树
# 的是
# 再用
# 只需
# 最常用
# 而不是
# 组里
# 升序
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道
高端网站建设与定制开发一站式解决方案 中企动力
怎么用AI帮你设计一套个性化的手机App图标?
如何在腾讯云服务器上快速搭建个人网站?
Android okhttputils现在进度显示实例代码
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
如何快速搭建高效服务器建站系统?
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法
Laravel观察者模式如何使用_Laravel Model Observer配置
Edge浏览器怎么启用睡眠标签页_节省电脑内存占用优化技巧
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
Win11怎么更改系统语言为中文_Windows11安装语言包并设为显示语言
Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
Laravel如何实现API速率限制?(Rate Limiting教程)
Laravel如何集成微信支付SDK_Laravel使用yansongda-pay实现扫码支付【实战】
动图在线制作网站有哪些,滑动动图图集怎么做?
如何在云主机快速搭建网站站点?
如何快速配置高效服务器建站软件?
高性能网站服务器配置指南:安全稳定与高效建站核心方案
Bootstrap CSS布局之列表
Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】
Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】
如何为不同团队 ID 动态生成多个独立按钮
Laravel怎么清理缓存_Laravel optimize clear命令详解
如何在自有机房高效搭建专业网站?
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
如何在阿里云部署织梦网站?
Win11怎么关闭资讯和兴趣_Windows11任务栏设置隐藏小组件
IOS倒计时设置UIButton标题title的抖动问题
Laravel如何处理文件下载请求?(Response示例)
Laravel如何设置定时任务(Cron Job)_Laravel调度器与任务计划配置
,在苏州找工作,上哪个网站比较好?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
作用域操作符会触发自动加载吗_php类自动加载机制与::调用【教程】
Android中AutoCompleteTextView自动提示
Laravel如何实现模型的全局作用域?(Global Scope示例)
nginx修改上传文件大小限制的方法
如何确认建站备案号应放置的具体位置?
如何在阿里云虚拟主机上快速搭建个人网站?
制作旅游网站html,怎样注册旅游网站?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法
Python3.6正式版新特性预览
Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层
Laravel怎么做缓存_Laravel Cache系统提升应用速度的策略与技巧
黑客如何通过漏洞一步步攻陷网站服务器?
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
通义万相免费版怎么用_通义万相免费版使用方法详细指南【教程】


