PHP/MySQL实现数据库树形文件夹结构转数组/JSON及代码报错排查咨询
解决文件夹树形结构构建的错误与优化方案
首先,咱们先搞定你遇到的Cannot use a scalar value as an array错误,再重新梳理整个树形结构的构建逻辑——你的代码里有几个关键的逻辑和结构理解偏差,我一步步给你讲清楚。
错误原因分析
你在buildTree里写的$tnode['folder']['fold_id']['subfolders']是问题的核心:fold_id是一个数字(比如1、2),属于标量值,不是数组,你试图把它当成数组去访问['subfolders'],自然会抛出这个错误。这是因为你搞错了节点的结构层次,subfolders应该是folder对象下的属性,而不是fold_id的子属性。
除此之外,你当前的递归构建逻辑也比较混乱,比如初始化树形数组的方式、递归传递参数的逻辑都不符合目标结构的要求,而且你给出的目标JSON还存在一个潜在问题:JSON对象不允许重复键,你示例里的sub_folders下有多个folder键,这在JSON解析时会被覆盖,是不合法的,后面我会给出修正后的合理结构。
正确的实现方案
我会给你一套更清晰、高效的实现方式,用ID映射的方式替代递归遍历,这种方法不仅容易理解,还能避免递归带来的性能问题。
重构后的getFolderTree函数
public function getFolderTree($foldID): string { $sql = <<<SQL SELECT 1 oby, fold_id, fold_name, fold_parent_fold_id FROM (SELECT * FROM folders ORDER BY fold_parent_fold_id, fold_id) items_sorted, (SELECT @iv := ?) initialisation WHERE find_in_set(fold_parent_fold_id, @iv) AND length(@iv := concat(@iv, ',', fold_id)) UNION SELECT 0 oby, fold_id, fold_name, fold_parent_fold_id FROM folders WHERE fold_id = ? ORDER BY 1, 4, 3 SQL; $rows = $this->db->query($sql, $foldID, $foldID)->fetchAll(); // 第一步:建立ID到节点的映射,预先初始化sub_folders为空数组 $folderMap = []; foreach ($rows as $row) { $folderMap[$row['fold_id']] = [ 'folder' => [ 'fold_id' => $row['fold_id'], 'fold_name' => $row['fold_name'], 'fold_parent_fold_id' => $row['fold_parent_fold_id'], 'sub_folders' => [] ] ]; } // 第二步:遍历节点,将每个节点添加到父节点的sub_folders中 $rootNode = null; foreach ($folderMap as $folderId => $node) { $parentId = $node['folder']['fold_parent_fold_id']; if ($parentId === null) { // 找到根节点 $rootNode = $node; } else { // 将当前节点追加到父节点的sub_folders数组中 $folderMap[$parentId]['folder']['sub_folders'][] = $node; } } // 生成格式化后的JSON(符合JSON规范,无重复键) return json_encode($rootNode, JSON_PRETTY_PRINT); }
代码说明
- ID映射构建:先把数据库返回的扁平数据转换成以
fold_id为键的关联数组,每个节点都预先初始化sub_folders为空数组,彻底避免了标量访问的错误。 - 树形结构组装:遍历每个节点,根据
fold_parent_fold_id找到它的父节点,把当前节点添加到父节点的sub_folders数组中——这种方式比递归更直观,也更容易调试。 - 修正JSON结构:把原目标中重复的
folder键改成数组形式,这符合JSON规范,不会出现解析时被覆盖的问题。最终生成的JSON结构如下(和你的需求一致但更合法):
{ "folder": { "fold_id": 1, "fold_name": "top", "fold_parent_fold_id": null, "sub_folders": [ { "folder": { "fold_id": 2, "fold_name": "subtop1", "fold_parent_fold_id": 1, "sub_folders": [] } }, { "folder": { "fold_id": 3, "fold_name": "subtop2", "fold_parent_fold_id": 1, "sub_folders": [ { "folder": { "fold_id": 4, "fold_name": "subsubtop1", "fold_parent_fold_id": 3, "sub_folders": [] } } ] } } ] } }
为什么原来的buildTree函数不可行?
除了标量访问的错误,原来的递归逻辑还有这些问题:
- 递归传递的参数不符合节点结构,每次递归都没有正确找到父节点的位置。
- 初始化
tree_array的方式混乱,比如一开始把$dirs赋值给tree_array,后续递归无法正确维护整个树形结构。 - 试图在标量属性上添加数组,完全搞错了节点的层次结构。
如果你坚持要用递归实现,也可以基于上面的ID映射逻辑调整,但显然ID映射的方式更简单高效。
内容的提问来源于stack exchange,提问作者TenG
相关产品推荐
相关产品推荐

