
本文介绍如何使用递归函数将具有任意深度嵌套关系的 category 对象树,转换为符合 json 序列化要求的关联数组结构,精确提取 id、title、url、parent 和 children 字段。
本文介绍如何使用递归函数将具有任意深度嵌套关系的 category 对象树,转换为符合 json 序列化要求的关联数组结构,精确提取 id、title、url、parent 和 children 字段。
在处理分类系统(如电商类目、内容导航菜单)时,常需将面向对象的嵌套结构(如 Category 实例及其子类目)序列化为前端可消费的扁平/嵌套数组格式。本教程提供一个健壮、可扩展的递归解决方案,支持任意层级嵌套,并严格遵循目标数据结构规范:
[
'5068' => [
'title' => 'Hardware',
'url' => 'https://example.com/Hardware',
'parent' => '12',
'children' => [
'316' => [
'title' => '3D Googles',
'url' => 'https://example.com/Hardware/3D-Googles',
'parent' => '5068',
'children' => []
],
'23' => [
'title' => 'CPUs & Cooler',
'url' => 'https://example.com/Hardware/CPUs-and-Cooler',
'parent' => '5068',
'children' => [
'26' => [
'title' => 'Cooler',
'url' => 'https://example.com/Hardware/CPUs-and-Cooler/Cooler',
'parent' => '23',
'children' => []
]
]
]
]
]
]
核心函数 buildTree() 采用尾递归友好设计,通过引用传参避免深层拷贝开销,同时清晰分离「当前节点构建」与「子树递归展开」逻辑:
/**
* 递归构建 Category 树形数组结构
*
* @param Category $oCat 待处理的根分类对象
* @param array &$aOutput 输出数组(内部递归使用引用)
* @param Category|null $oParent 当前节点的直接父对象(用于推导 parent 字段)
* @param bool $return 是否返回最终结果(顶层调用设为 true,递归调用设为 false)
* @return array|void
*/
function buildTree($oCat, &$aOutput = [], $oParent = null, $return = true) {
$actId = $oCat->getId();
// 构建当前节点基础字段
$aOutput[$actId] = [
'id' => $actId,
'title' => $oCat->getTitle(),
'url' => $oCat->getLink(),
'parent' => $oParent ? $oParent->getId() : (
$oCat->getParentCategory() ? $oCat->getParentCategory()->getId() : ':globalroot'
),
'children' => []
];
// 递归处理子分类
if ($oCat->getHasVisibleSubCats()) {
foreach ($oCat->getSubCats() as $sId => $oSubCat) {
// 预置子节点骨架(含 children 占位符)
$aOutput[$actId]['children'][$sId] = [
'id' => $sId,
'title' => $oSubCat->getTitle(),
'url' => $oSubCat->getLink(),
'parent' => $actId,
'children' => [] // 初始化为空数组,便于后续递归填充
];
// 若子节点仍有子分类,则递归构建其 children
if ($oSubCat->getHasVisibleSubCats()) {
buildTree($oSubCat, $aOutput[$actId]['children'][$sId], $oCat, false);
}
}
}
return $return ? $aOutput : null;
}
✅ 关键设计亮点:
-
明确父子关系推导:优先使用传入的
$oParent(保证链式调用一致性),回退至$oCat->getParentCategory(),最后兜底为':globalroot',避免空指针异常; -
children 始终为数组类型:统一初始化为
[]而非null,确保 JSON 序列化后结构稳定(json_encode([])→[],而json_encode(null)→null); -
无副作用参数设计:
$aOutput以引用方式传递,$oParent和$return作为纯控制参数,接口语义清晰; -
零冗余遍历:每个
Category实例仅被访问一次,getSubCats()返回的数组直接用于循环,不重复调用 getter。
? 使用示例:
// 假设 $oBaseCat 是根分类对象 $treeArray = buildTree($oBaseCat); echo json_encode($treeArray, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
⚠️ 注意事项:
- 确保
Category::getSubCats()返回的是Category对象数组(键为 ID),否则需在循环内做类型校验; - 若存在循环引用(如误配置的父子关系),需额外加入已访问 ID 集合(
$visited = [])进行防重检测; - 生产环境建议增加类型声明(PHP 7.4+)和参数校验,例如
assert($oCat instanceof Category)。
该方案兼顾可读性、健壮性与性能,可直接集成至 CMS、API 层或模板渲染逻辑中,是处理多级分类数据结构的经典实践。










