如何查询包含产品的所有子类别(直接在子类别中,或在类别树下的任何位置,例如在子子类别中)?

时间:2012-11-27 14:23:04

标签: symfony doctrine-orm dql nested-sets query-builder

架构:

  

类别[ idparentchildrenleftrightlevel,{{ 1}},root ]

     

产品[ productsidcategory ]

     

MarketSegment [ marketSegments ]

     

此外,每个实体都有 id name description 等字段但那些与我的问题无关。

示例数据

图例:

slug - 类别,c - 产品

标有*的产品标有细分市场“Export to USA”

p

预期的查询结果:

对于给定数据和 $ category = 食品 $ marketSegment = 导出到美国预期结果将是:

Food [c, id: 1, level: 0] ---> Vegetables [c, id: 2, level: 1] --------------> Potato [p, id: 1] --------------> Carrot [p, id: 2] ---> Fruits [c, id: 3, level: 1] --------------> Berries [c, id: 5, level: 2] ---------------------------> Grapes [p, id: 3] --------------> Hesperidiums [c, id: 6, level: 2] ---------------------------> Orange* [p, id: 4] ---> Meat [c, id: 4, level: 1] --------------> Beef* [p, id: 5]

为什么?

  • $filteredCategories = [Fruits, Meat]因为它包含标有导出到美国的产品Meat
  • Beef因为它包含类别Fruits,其中包含标记产品。

嵌套树中包含标记产品的类别的深度无关紧要。

此:

Hesperidiums

对于 $ category = 体育应该返回:[Sports [c] -----> Individual [c] ----------------> Fight [c] ----------------------> MMA [c] --------------------------> Boxing gloves* [p] ]。

对于 $ category = 战斗应返回:[Individual]。


我的(不工作)DQL方法:

MMA

2 个答案:

答案 0 :(得分:3)

随Gedmo NestedSet行为扩展一起提供的

NestedTreeRepository有一个方法getChildrenQueryBuilder(),允许您查询给定父节点的子节点,并且有一个参数用于查询多个深层的节点层次结构。您应该调查NestedTreeRepository附带的方法和选项,以便不需要手动构建DQL。

我不完全确定您的所有代码是如何设置的,因此我无法提供准确的解决方案,但我认为您可以解决这个问题:)

NestedSet文档:http://www.gediminasm.org/article/tree-nestedset-behavior-extension-for-doctrine-2

答案 1 :(得分:1)

谢谢约翰!使用NestedTreeRepository我找到了解决方案:

我为Category类创建了自己的Repository,它扩展了NestedTreeRepository并创建了一个自定义方法

  

public function findSubcategoriesFilteredByMarketSegment(Category $ parentNode,MarketSegment $ segment)

     

{

   // get DIRECT CHILDREN for $parentNode
   $qb = $this->getChildrenQueryBuilder($parentNode, true);
   $qb->leftJoin($qb->getRootAlias().'.products', 'prod')
      ->leftJoin('prod.marketSegments', 'seg');    

   // subquery -> get all children (direct and indirect) 
   // for DIRECT CHILD from main query
   $sqb = $this->_em->getRepository('AvocodeCatalogBundle:Category')->createQueryBuilder('sub');

   // join products and market segments
   $sqb->leftJoin($sqb->getRootAlias().'.products', 'sub_prod')
       ->leftJoin('sub_prod.marketSegments', 'sub_seg');

   // to match children of main query's DIRECT CHILD
   // they need to have left and right values between DIRECT CHILD's left and right
   $sqb->where(
            $sqb->expr()->andX(
               $sqb->expr()->gt('sub.lft', $qb->getRootAlias().'.lft'),
               $sqb->expr()->lt('sub.rgt', $qb->getRootAlias().'.rgt'),
               $sqb->expr()->eq('sub.root', $qb->getRootAlias().'.root')
        ));

   // match only children with products related to $segment
   $sqb->andWhere($sqb->expr()->eq('sub_seg.id', ':segment_id'));

   // BACK TO MAIN QUERY
   // select only parentNode children THAT
   $qb->andWhere(
         $qb->expr()->orX(
             // contain product with related segment
             $qb->expr()->eq('seg.id', ':segment_id'),
             // or 
             // have a child (direct or indirect) that does contain such product
             $qb->expr()->exists($sqb->getDQL())       
     ))
     ->setParameter('segment_id', $segment->getId());

   return $qb->getQuery()->getResult();
     

}