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

如何自动获取指定ID分类节点的所有父节点(Symfony+Gedmo-Tree)

解决指定分类节点及其所有父节点的自动获取问题

我明白你现在的困扰——想自动获取指定分类的所有父节点,而不是靠手动加根节点的临时方案凑数。之前尝试的->orWhere('node.id in (node.parent)')报错,是因为这种写法逻辑不对:node.parent是单个字段(对应父节点ID),不是集合,而且树形结构的父节点需要递归查询,不是简单的WHERE条件能搞定的。

下面给你两种靠谱的解决方案,按需选择:

方案一:用递归CTE(推荐,支持MySQL 8+/PostgreSQL)

这是最优雅的方式,利用数据库的递归公共表表达式(CTE)自动遍历所有父节点,完全不需要手动处理层级逻辑。

单个分类ID的场景

public function getCategoryWithAncestors(int $categoryId): array
{
    $em = $this->getEntityManager();
    $connection = $em->getConnection();

    // 递归CTE:先取目标节点,再递归关联父节点直到根节点
    $sql = <<<SQL
WITH RECURSIVE category_hierarchy AS (
    SELECT id, name, parent_id  -- 替换成你的Category实体实际字段
    FROM category
    WHERE id = :categoryId
    UNION ALL
    SELECT c.id, c.name, c.parent_id
    FROM category c
    JOIN category_hierarchy ch ON c.id = ch.parent_id
)
SELECT * FROM category_hierarchy
ORDER BY parent_id IS NULL DESC, parent_id, id;  -- 根节点排最前,按层级排序
SQL;

    $stmt = $connection->prepare($sql);
    $stmt->bindValue('categoryId', $categoryId);
    $stmt->execute();

    return $stmt->fetchAllAssociative();
}

多个分类ID的场景(适配你原函数的需求)

如果需要同时处理多个分类ID,只需修改CTE的初始查询条件:

public function getCategoriesWithAncestors(array $categoryIds): array
{
    $em = $this->getEntityManager();
    $connection = $em->getConnection();

    // 生成安全的占位符,避免SQL注入
    $placeholders = implode(',', array_fill(0, count($categoryIds), '?'));

    $sql = <<<SQL
WITH RECURSIVE category_hierarchy AS (
    SELECT id, name, parent_id
    FROM category
    WHERE id IN ($placeholders)
    UNION ALL
    SELECT c.id, c.name, c.parent_id
    FROM category c
    JOIN category_hierarchy ch ON c.id = ch.parent_id
)
SELECT * FROM category_hierarchy
ORDER BY parent_id IS NULL DESC, parent_id, id;
SQL;

    $stmt = $connection->prepare($sql);
    $stmt->execute($categoryIds);

    return $stmt->fetchAllAssociative();
}

方案二:利用Doctrine Tree扩展(如果已集成)

如果你已经用了DoctrineExtensions的Tree组件(比如Adjacency List模式),可以直接用现成方法获取父节点:

use Gedmo\Tree\Entity\Repository\NestedTreeRepository;

// 确保你的Category仓库继承自NestedTreeRepository
public function getCategoryWithAncestors(int $categoryId): array
{
    $category = $this->find($categoryId);
    // 获取所有父节点(第二个参数true表示包含自身)
    return $this->getAncestors($category, true);
}

把平级结果转成树形结构

如果需要把查询到的平级节点转成嵌套的树形结构,可以用递归函数处理:

private function buildTree(array $items, $parentId = null): array
{
    $tree = [];
    foreach ($items as $item) {
        if ($item['parent_id'] === $parentId) {
            $children = $this->buildTree($items, $item['id']);
            if (!empty($children)) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
    }
    return $tree;
}

// 使用示例:
$flatResult = $this->getCategoriesWithAncestors([1,3,5]);
$treeResult = $this->buildTree($flatResult);

这样就完全替代了手动获取根节点的临时方案,自动递归获取所有父节点啦!

内容的提问来源于stack exchange,提问作者emovere

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:34:05