Doctrine2:多对多联合实体,多个和哪些或者加入WITH子句?

时间:2015-11-03 14:26:06

标签: symfony join doctrine-orm many-to-many multiple-conditions

两个doctrine2实体,Photo和Tag,通过多对多关系链接,并相应地进行映射。

每个标记都有一个键和一个值,因此示例键为“photo-type”,示例值为“people”。

我创建了一个自定义存储库PhotoRepository.php,以便于使用标记对的数组(或逗号分隔列表*)轻松搜索照片,如下所示:

 public function getQueryByTags($tags = null, $limit =0, $start =-1, $boolean ="and")
   {
    ...
        $qb->select('p')
                ->from('MyBundle\Entity\Photo', 'p')
                ->join('p.tags','t');
                # ->join('a.tags','t')
        if ($boolean == "and") { $qb->where('1 = 1'); }
        $i = 1;
        foreach ($tags as $tag) {
            if ($boolean == "and") { 
                $qb->andWhere('t.key = ?'.$i.' AND t.value = ?'.($i+1));
            } elseif ($boolean == "or") { 
                $qb->orWhere('t.key = ?'.$i.' AND t.value = ?'.($i+1));
            }
            $qb->setParameter($i, $tag['key'])->setParameter(($i+1), $tag['value']);
            $i += 2;
        }
    ...
      return $qb->getQuery();
   }

这适用于单个标签。但是,一旦标记为多个(例如,搜索'photo-type'=>'people','person'=>'Bob'),布尔逻辑就会崩溃并且不会返回任何结果。

我怀疑这与将加入的Tag实体与Doctrine2 queryBuilder()放在一起的Where /(或Where)子句有关。 (因为相同的标签不能同时'照片类型'=>'人'和'人'=>'鲍勃',尽管相同的照片应该是)。

    $photos = $em->getRepository('MyBundle:Photo')->
            findByTags(array(
                array('key' => 'context','value' => $context),
                array('key' => 'photo-type','value' => 'field'),
            ));

我尝试构建一个JOIN WITH查询,但这似乎需要一个非常复杂的构造来创建表达式,我无法弄清楚: -

public function getQueryByTags($tags = null, $limit =0, $start =-1, $boolean ="and")
    {
    ...
        $qb->select('p')
                ->from('MyBundle\Entity\Photo', 'p');
        $i = 1;
        $expr = $qb->expr();

        foreach ($tags as $tag) {
            if ($boolean == "and") { 
                $expr = $expr->andX($qb->expr()->eq('t.key', '?'.$i),$qb->expr()->eq('t.value', '?'.($i+1)));                    
            } elseif ($boolean == "or") { 
            }
            $qb->setParameter($i, $tag['key'])->setParameter(($i+1), $tag['value']);
            $i += 2;
        }

        $qb->join('p.tags','t',Doctrine\ORM\Query\Expr\Join::WITH,$expr);
                # ->join('a.tags','t')
      ...
      return $qb->getQuery();
   }

编辑:最终我想要的结果是:

  • “和”搜索:SELECT all Photos which have (Tag with key(A) AND value(B) ) AND (another Tag with key(C) AND value(D))

  • “或”搜索:SELECT all Photos which have (Tag with key(A) AND value(B) ) OR (another Tag with key(C) AND value(D))

  • 其中:A,B是第一个唯一标签'对'(例如'photo-type'='people'或'photo-type'='animal')而C,D是另一个唯一标签'pair'(例如'person'='Bob','person'='Tiddles'或理论上'animal'='Tiddles')

任何人都可以帮我弄清楚如何构建这个复杂的JOIN WITH表达式吗?

或者,如果看起来我正在咆哮错误的树,那么有人可以提出另一种更优雅的做法吗?

*注意:如果以逗号分隔的字符串收到$ tag(例如$ tags =“photo-type = people,person = Bob”),则首先将其转换为数组。

编辑#2:Tag实体,应@Wilt请求:

Tag.yml

MyBundle\Entity\Tag:
    type: entity
    table: tag
    fields:
        id:
            id: true
            type: integer
            unsigned: true
            nullable: false
            generator:
                strategy: IDENTITY
        key:
            type: string
            length: 20
            fixed: false
            nullable: true
        value:
            type: string
            length: 50
            fixed: false
            nullable: true
    manyToMany:
        photos:
            targetEntity: Tag
            mappedBy: tags
    lifecycleCallbacks: {  }

Photo.yml(仅限提取物)

MyBundle\Entity\Photo:
    type: entity
    repositoryClass: MyBundle\Entity\PhotoRepository
    table: photo
    fields:
        sha1:
....
    manyToMany:
        tags:
            targetEntity: Tag
            inversedBy: photos
            joinTable:
                name: x_photo_tag
                joinColumns:
                    photo_sha1:
                        referencedColumnName: sha1
                inverseJoinColumns:
                    tag_id:
                        referencedColumnName: id

1 个答案:

答案 0 :(得分:1)

对于像这样的事情,你的代码看起来太复杂了。 您可以使用本机doctrine解决方案来使用in array解决方案检查标记类型:

$array = [];
foreach ($tags as $i => $tag) {
    $array[] = $tag['value'];
}
$qb = $this->createQueryBuilder('p')
           ->innerJoin('p.tags','t')
           ->where('t.type IN(:array)')

或者我误解了你的情况?然后尝试更清楚一下你真正想要的结果集。

修改

我认为你可以这样做:

// Main query
$qb = $this->createQueryBuilder('p')
           ->innerJoin('p.tags','t');

// Get tag repository to make tag query (you can also use FROM instead)
$tagRepository = $this->getEntityManager()->getRepository('MyBundle\Entity\Tag');

// Choose your where
$where = 'orWhere';  //`OR` query:
$where = 'andWhere'; //`AND` query:

// Add a in sub query expression for each tag
foreach ($tags as $i => $tag){
    $alias = 't' . $i;
    $sub = $tagRepository->createQueryBuilder($alias);
    $sub->where($alias . '.key = :key' . $i);
    $sub->andWhere($alias . '.value = :value' . $i);
    $qb->setParameter('key' . $i, $tag['key']);
    $qb->setParameter('value' . $i, $tag['value']);
    $qb->$where($qb->expr()->in('t.id', $sub->getDQL()));
}

// get your resultset
return $qb->getQuery();