如何用递归替代PHP多层嵌套Foreach遍历文件夹及子文件夹ID?
问题描述
目前我用多层嵌套的foreach循环来获取文件夹及子文件夹的ID,用于执行删除操作,但每一层都要写foreach,代码太冗余了。请问怎么用简洁的循环或函数实现?
原代码:
// first foreach code $idsToDelete = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId'=>$postId]); foreach ($idsToDelete as $d){ $postId = $d->getId(); echo $postId.', '; //second foreach code in first foreach $idsToDelete = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId'=>$postId]); foreach ($idsToDelete as $d){ $postId = $d->getId(); echo $postId.', '; //third foreach code in second foreach $idsToDelete = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId'=>$postId]); foreach ($idsToDelete as $d){ $postId = $d->getId(); echo $postId.', '; //forth foreach code in third foreach $idsToDelete = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId'=>$postId]); foreach ($idsToDelete as $d){ $postId = $d->getId(); echo $postId.', '; //fith foreach code in forth foreach $idsToDelete = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId'=>$postId]); foreach ($idsToDelete as $d){ $postId = $d->getId(); echo $postId.', '; } } } } }
解决方案
方法一:递归函数
利用递归可以轻松遍历任意深度的树形结构,代码简洁且可维护:
/** * 递归获取所有子文件夹ID * @param int $parentId 父文件夹ID * @param array $ids 用于存储收集到的ID * @return array */ private function getAllChildFolderIds(int $parentId, array &$ids = []): array { // 获取当前父文件夹下的所有子文件夹 $childFolders = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId' => $parentId]); foreach ($childFolders as $folder) { $folderId = $folder->getId(); $ids[] = $folderId; // 递归获取当前子文件夹的子文件夹ID $this->getAllChildFolderIds($folderId, $ids); } return $ids; } // 使用示例 $postId = // 你的根父ID $allIdsToDelete = $this->getAllChildFolderIds($postId); // 输出所有ID(可选) echo implode(', ', $allIdsToDelete);
说明:
- 递归函数会自动遍历所有层级的子文件夹,不管嵌套多深都能覆盖
- 通过引用传递
$ids数组,避免每次递归创建新数组,提升效率 - 后续执行删除操作时,直接使用
$allIdsToDelete即可
方法二:迭代法(队列/栈)
如果担心递归深度过大导致栈溢出(比如文件夹层级极深),可以用迭代的方式,借助队列或栈来实现广度优先/深度优先遍历:
广度优先遍历(队列实现)
private function getAllChildFolderIds(int $parentId): array { $ids = []; $queue = [$parentId]; while (!empty($queue)) { $currentParentId = array_shift($queue); // 获取当前父文件夹下的子文件夹 $childFolders = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId' => $currentParentId]); foreach ($childFolders as $folder) { $folderId = $folder->getId(); $ids[] = $folderId; // 将子文件夹ID加入队列,继续遍历其子文件夹 $queue[] = $folderId; } } return $ids; } // 使用示例 $postId = // 你的根父ID $allIdsToDelete = $this->getAllChildFolderIds($postId); echo implode(', ', $allIdsToDelete);
深度优先遍历(栈实现)
和队列类似,只是用array_pop()代替array_shift():
private function getAllChildFolderIds(int $parentId): array { $ids = []; $stack = [$parentId]; while (!empty($stack)) { $currentParentId = array_pop($stack); $childFolders = $this->entityManager->getRepository(Folders::class) ->findBy(['parentId' => $currentParentId]); foreach ($childFolders as $folder) { $folderId = $folder->getId(); $ids[] = $folderId; $stack[] = $folderId; } } return $ids; }
说明:
- 迭代法不会有递归栈溢出的问题,适合处理层级极深的文件夹结构
- 广度优先会按层级从上到下遍历,深度优先则是先遍历完一个分支再处理下一个
额外优化建议
如果ORM支持,可以直接写递归SQL或DQL一次性查询所有子文件夹ID,减少数据库查询次数,提升性能。比如用MySQL的WITH RECURSIVE语法:
WITH RECURSIVE folder_tree AS ( SELECT id FROM folders WHERE parent_id = :parentId UNION ALL SELECT f.id FROM folders f JOIN folder_tree ft ON f.parent_id = ft.id ) SELECT id FROM folder_tree;
在Doctrine中可以用原生SQL或DQL实现这个查询,避免多次查询数据库。
内容的提问来源于stack exchange,提问作者dino
相关产品推荐
相关产品推荐

