【问题标题】:Doctrine2: many-to-many joined entities, multiple andWhere or join WITH clauses?Doctrine2:多对多连接实体,多个 andWhere 或连接 WITH 子句?
【发布时间】:2015-11-03 14:26:06
【问题描述】:

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

每个标签都有一个键和一个值,因此示例键是“照片类型”,示例值是“人”。

我创建了一个自定义存储库 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 实体中的 andWhere/(orWhere) 子句与 Doctrine2 queryBuilder() 放在一起有关。 (因为同一个标签不能同时是 'photo-type'=>'people' AND 'person'=>'Bob',尽管应该是同一张照片)。

    $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 是第一个唯一标签“对”(例如“照片类型”=“人”或“照片类型”=“动物”),C、D 是另一个唯一标签'pair'(例如,'person'='Bob'、'person'='Tiddles' 或者,理论上 'animal'='Tiddles')


谁能帮我弄清楚如何构造这个复杂的 JOIN WITH 表达式?

或者,如果我好像找错了树,谁能提出一种更优雅的替代方法?

*NB:如果 $tags 是以逗号分隔的字符串形式接收的(例如 $tags="photo-type=people,person=Bob"),它首先被转换成一个数组。


EDIT#2:标签实体,应@Wilt 的要求:

标签.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

【问题讨论】:

  • 解释两部分标签的逻辑: ——理论上这个标签对可以建模为一个唯一的字符串(而不是一个唯一的Tag: key, value;一个唯一的Tag: 'key=value')但是数据源(Web 应用程序外部)的性质使得分别存储键和值更容易进行排序。 (即,可以对键或值字段进行排序)。 –– 它还允许应用程序识别其他对象以将标签值链接回(例如,'person'、'Bob' 或 'person'、'Jill' 到某个索引的 'Person' 对象)。
  • 可以在一张图片上添加标签'photo-type'='people''animal'='Tiddles'吗?否则,我建议更改您的模型,让人物图片带有人物标签,动物图片带有动物标签。这将使这一切变得容易得多。
  • 是的,'photo-type'='people''animal'='Tiddles' 应该是可能的,因为标签是人工创建的,而不是 db 模型在语义上预先确定的。 (这些标签对只是示例)。

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


【解决方案1】:

对于这样的事情,您的代码看起来太复杂了。 您可以使用原生学说解决方案通过数组解决方案检查您的标签类型:

$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();

【讨论】:

  • 非常感谢您的建议。澄清一下,标签实体有两个字段,必须搜索“t.key”和“t.value”,因此增加了复杂性。我想要的结果集是:-“和”搜索:查找所有具有 (key(X) AND value(Y)) 标签和另一个带有 key(C) AND value(D) 的标签的照片对象 - “或" search: 查找所有具有 (key(X) AND value(Y) ) 标记或另一个具有 key(C) AND value(D) 标记的照片对象
  • @TCW 例如,在第一个查询中,您需要带有key(A) + value(B) AND key(C) + value(D) 的标签。 A + B 是唯一的吗? C + D 是独一无二的吗?或者是否还有一个带有键 A 和值 D 或键 C 和值 B 的标签?
  • 如果我正确理解您的问题,是的,所有“标签对”示例都是唯一的(即,您不能有两个具有相同键和值的标签)。 [在MySQL后端,两列被记录为唯一索引/键]
  • 我更新了我的答案。它未经测试,但认为它应该可以工作,如果有问题,您可以发表评论...
  • 再次感谢。这比我原来的要优雅得多。一些小的调整: $sub->setParameter 需要是 $qb->setParameter (否则 Doctrine 会抱怨参数数量不匹配);另外我必须添加$i = 1$i +=1;....**但是**,不幸的是,结果集仍然无法通过两个或多个标签找到照片。搜索'photo-type'='people' 会找到照片;搜索 'people'='Bob' 会找到照片,但同时搜索(使用 AND 查询)会得到 0 个结果,尽管某些照片具有两个标签。令人毛骨悚然的时刻?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多