PHP+MySQL实现无限下级分类树:递归与引用法对比
探讨在 PHP 和 MySQL 环境下构建无限下级分类树的策略,涵盖递归查询实现与基于数组引用的一次性数据处理方法。
核心逻辑与准备条件
无限下级分类(通常称为无限极分类或树形结构)的核心在于如何建立分类之间的上下级关系。实现这一目标通常需要满足以下基础:
- 数据库表设计:包含唯一标识字段
id、分类名称name以及父级 IDpid。顶级分类的pid通常设为0。 - 数据结构目标:将扁平的一维数据转化为嵌套的树状结构,通常表现为数组中增加
children键来存储子节点。
第一种方案:递归查询输出(适用于小规模数据)
这种方法思路直观,即每发现一个节点,就去数据库查询其是否有子节点,以此类推。其优点是代码逻辑简单,缺点是存在大量重复 SQL 查询,性能较差。
示例:递归函数获取树形 JSON
定义函数 get_array($user_id, $top),其中 $top=1 表示开始遍历顶层。该函数执行 SQL 查询,组装数组并递归调用自身。
function get_array($user_id,$top=0){
global $mysql,$_G;
// 注意:生产环境请确保变量已正确转义防止 SQL 注入
$sql = "select user_id as name from `{spreads_users}` where spreads_userid='{$user_id}'";
$rows = $mysql->db_fetch_arrays($sql);
if($top==1){
$arr[0]['name']=$user_id;
$arr[0]['children']=array();
}
$top=$top+1;
foreach ($rows as $key=>$value)
{
$r = get_array($value['name']); // 递归调用,查询下级
$arr[0]['children'][$key]['name']= $value['username'];
if(is_array($r)){
$arr[0]['children'][$key]['children']= $r[0]['children'];
}
$i++;
}
return $arr;
}
// 调用示例:从 ID 为 1000 的顶级分类开始
$list = get_array("1000",1);
echo 'var data='.json_encode($list);
第二种方案:引用法构建(高效推荐)
为了避免 N+1 次 SQL 查询的性能陷阱,最佳实践是先查出所有分类数据到 PHP 内存中,然后进行一次遍历构建树结构。这利用了 PHP 引用机制(&),以极低的 CPU 开销完成层级映射。
Step 1: 构建原始一维数组
假设从数据库中拉取出的数据如下(也可在实际应用中替换为真实的 DB 查询结果):
$categories = array(
array('id'=>1,'name'=>'电脑','pid'=>0),
array('id'=>2,'name'=>'手机','pid'=>0),
array('id'=>3,'name'=>'笔记本','pid'=>1),
array('id'=>4,'name'=>'台式机','pid'=>1),
array('id'=>5,'name'=>'智能机','pid'=>2),
array('id'=>6,'name'=>'功能机','pid'=>2),
array('id'=>7,'name'=>'超级本','pid'=>3),
array('id'=>8,'name'=>'游戏本','pid'=>3),
);
Step 2: 初始化索引数组
将 id 作为 key,同时初始化空的 children 数组:
$tree = array();
foreach($categories as $category){
$tree[$category['id']] = $category;
$tree[$category['id']]['children'] = array();
}
Step 3: 利用引用归并子节点
这是最关键的一步。再次遍历数组,如果当前节点的 pid 不为 0,则将其引用添加到父节点的 children 数组中:
// 利用引用,一次遍历即可形成树形结构
foreach ($tree as $k=>$item) {
if ($item['pid'] != 0) {
// 关键:使用 &$tree[$k] 将引用指向原元素,而非复制副本
$tree[$item['pid']]['children'][] = &$amp;$tree[$k];
}
}
执行后,$tree[1] 的 children 中将包含“笔记本”和“台式机”的结构,且它们内部会继续包含更深层的引用。
其他常见写法辨析
部分旧代码中使用了字符串拼接模拟树形缩进(如 │ │ └─),这种做法属于前端显示层面的格式化,并非标准的数据结构处理:
function sort_list($str,$fatherid,$count){
// ... 查询数据 ...
for($n = 1 ; $n < $count ; $n++){
$name.="│ "; // 层级前缀
}
// ... 组合字符串并递归 ...
$str = $this->sort_list($str,$rs[$i][id],$temp);
return $str;
}
这种方法的缺点是无法直接获得可操作的树形对象(JSON/Array),不利于后续的前端组件渲染(如 Vue/React TreeView)或二次加工。
性能优化与总结
- 缓存策略:分类数据通常变动频率低。建议在应用层将生成的树形数组序列化保存至 Redis 或文件缓存中,避免每次请求都进行 PHP 内存计算。
- 数据库路径记录:对于超大型分类库,除了父子指针法(PID),还可考虑维护
path字段(如/1/3/7/)以加速特定深度的过滤操作。 - 适用场景:递归查询仅适合极少量级(如几十级)且频繁变动的场景;引用法(全量加载后处理)是 Web 开发中最稳健的标准解法。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













