You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用递归替代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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:15:31