Doctrine ORM按标签列表选择文章

时间:2014-06-02 16:51:19

标签: symfony doctrine-orm tags many-to-many dql

我有一个Webinar实体与Tag有多对多关联。

我想通过标签列表获取网络研讨会,因此网络研讨会必须让列表中的所有标签都显示在结果中。

我有以下代码来查询它:

$tags = [1, 2];

// Base query.
$builder = $this->getEntityManager()->createQueryBuilder()
    ->select('W, VR, AR, T')
    ->from('Model:Webinar',     'W')
    ->leftJoin('W.videoRecord', 'VR')
    ->leftJoin('W.audioRecord', 'AR')
    ->leftJoin('W.tags',        'T')
;

// Tags query.
if (isset($tags)) {
    $builder
        ->andWhere('T.id IN (:tags)')
        ->setParameter('tags', $tags)
    ;
}

// Other conditions.
// ...

// Issuing the query.
return $builder->getQuery()->getResult();

此查询将为我提供列表中至少有一个标记(OR)的所有网络研讨会,但我希望它在列表中包含所有标记(AND)。

我发现了answer。但看起来我有一个更复杂的例子,我不知道如何重新编写我的代码以启用正确的行为,我有其他连接的实体和其他条件。是否可以在单个块中隔离此行为而不会破坏整个查询和其他条件?

2 个答案:

答案 0 :(得分:3)

我无法在单个查询中同时使用聚合和提取,因此我决定将其拆分为两个。

通过使用第一个查询,我将应用所有必要条件以接收网络研讨会ID列表,并使用第二个查询我将获取这些ID的对象图。

以下是完整的示例:

// I. Making the first query to fetch only webinar IDs.

// Base query.
$builder = $this->getEntityManager()->createQueryBuilder()
    ->select('W.id')
    ->from('Model:Webinar', 'W')
    ->orderBy('W.' . $sort, $order)
;

// Selected tags condition.
if (is_array($criteria['tags']) && count($criteria['tags']) > 0) {
    $builder
        ->innerJoin('W.tags', 'T', 'WITH', 'T.id IN (:tags)')
        ->setParameter('tags', $criteria['tags'])
        ->groupBy('W.id')
        ->having('COUNT(T.id) = :tags_count')
        ->setParameter('tags_count', count($criteria['tags']))
    ;
}

// Any additional conditions
// ...

// Building the list of webinar IDs.
$ids = [];
foreach ($builder->getQuery()->getScalarResult() as $record) {
    $ids[] = $record['id'];
}

// II. And now selecting complete object graph for specified webinar IDs.

// Base query.
$builder = $this->getEntityManager()->createQueryBuilder()
    ->select('W, VR, AR, T')
    ->from('Model:Webinar',       'W')
    ->leftJoin('W.videoRecord',   'VR')
    ->leftJoin('W.audioRecord',   'AR')
    ->leftJoin('W.tags',          'T')
    ->where('W.id IN (:ids)')
    ->setParameter('ids', $ids)
;

// Issuing the query.
return $builder->getQuery()->getResult();

我希望它会帮助别人。干杯!

答案 1 :(得分:1)

这是sql的IN子句的工作方式,它会占用ANY列表中IN个项目的所有行。你必须像你提供的答案那样做类似的事情,所以在你的情况下你的查询看起来像:

// Base query.
$builder = $this->getEntityManager()->createQueryBuilder()
    ->select('W, VR, AR, T')
    ->from('Model:Webinar',     'W')
    ->leftJoin('W.videoRecord', 'VR')
    ->leftJoin('W.audioRecord', 'AR')
    ->leftJoin('W.tags',        'T')
;

// Tags query.
if (isset($tags)) {
    $builder
        ->andWhere('T.id IN (:tags)')
        ->setParameter('tags', $tags)
    ;


    $builder->groupBy('T.id')
        ->having("COUNT(DISTINCT T.id) = :count")
        ->setParameter("count", count($tags));
}

这应该可以正常工作我猜