栏目、菜单、地区这类数据在数据库里通常是 parent_id 扁平行,前端展示却需要树形或面包屑。用递归把它们组装好,一次封装长期受益。
实现思路
buildTree 递归遍历列表,把 parent_id 等于当前节点的元素挂到 children 下;findPath 先建 id 索引,再沿 parent_id 逐级上溯收集路径,最后用 array_unshift 保证顺序从根到本节点。
完整代码
<?php
/**
* 扁平列表转树形结构
* @param array $items 数据行,每行需含 id、parent_id
*/
function buildTree(array $items, int $parentId = 0): array
{
$tree = [];
foreach ($items as $item) {
if ((int)$item["parent_id"] === $parentId) {
$children = buildTree($items, (int)$item["id"]);
if ($children) {
$item["children"] = $children;
}
$tree[] = $item;
}
}
return $tree;
}
/**
* 查找某节点的面包屑路径名称
*/
function findBreadcrumb(array $items, int $id): array
{
$map = [];
foreach ($items as $item) {
$map[$item["id"]] = $item;
}
$path = [];
$cursor = $map[$id] ?? null;
while ($cursor) {
array_unshift($path, $cursor["name"]);
$cursor = $map[$cursor["parent_id"]] ?? null;
}
return $path;
}
// 以 ThinkPHP 为例取全表
// use thinkfacadeDb;
// $list = Db::name("cms_category")->order("sort asc, id asc")->select()->toArray();
// $tree = buildTree($list);
// print_r($tree);
// print_r(findBreadcrumb($list, 88));
?>边界说明
- buildTree 未对孤儿数据做防御,若出现 parent_id 不存在的脏数据会被丢弃,正式环境可在入库前校验;
- 递归在万级节点下效率尚可,超大数据量建议改内存索引两次遍历方案;
- findBreadcrumb 返回的是名称数组,如需带 id 的可自行调整。
小结
递归转树与上溯路径是 CMS 开发的固定套路,配合排序字段即可直接驱动菜单与面包屑组件。
评论(0)
暂无评论,快来抢沙发吧~