如何 select 具有 ManyToMany 关系中的两个项目的行
How to select rows which have both items in ManyToMany relation
假设我有“新闻”实体,它具有多对多“标签”关系
class News
{
/**
* @ORM\ManyToMany(targetEntity="App\Domain\Entity\Vocabulary\Tag")
*/
private Collection $tags;
}
我有这样的疑问:
public function getList(
array $tags = null,
): Query {
if (null !== $tags) {
$qb->andWhere('nt.id IN (:tags)');
$qb->setParameter('tags', $tags);
}
}
问题是当我传递 ["Tag1", "Tag2"] 时,selects 新闻具有第一个标签或第二个标签,但不能同时具有这两个标签。我如何将查询重写为同时具有两个标签的 select 新闻?
首先要注意的一些事情:
对于学说注释,可以使用 ::class
-常量:
use App\Domain\Entity\Vocabulary\Tag;
class News
{
/**
* @ORM\ManyToMany(targetEntity=Tag::class)
*/
private Collection $tags;
}
如果 $tags
数组为空,则将抛出异常,因为空值集无效 SQL,至少在 mysql:
nt.id IN () # invalid!
现在问题来了:
使用SQL聚合函数COUNT
和GROUP BY
我们可以统计所有新闻的标签数量。连同您对允许标签的条件,每个新闻的标签数量必须等于标签数组中的标签数量:
/**
* @var EntityManagerInterface
*/
private $manager;
...
/**
* @param list<Tag> $tags - Optional tag filter // "list" is a vimeo psalm annotation.
*
* @return list<News>
*/
public function getNews(array $tags = []): array
{
$qb = $this->manager
->createQueryBuilder()
->from(News::class, 'news')
->select('news')
;
if(!empty($tags)) {
$tagIds = array_unique(
array_map(static function(Tag $tag): int {
return $tag->getId();
}) // For performance reasons, give doctrine ids instead of objects.
); // Make sure duplicate tags are handled.
$qb
->join('news.tags', 'tag')
->where('tag IN (:tags)')
->setParameter('tags', $tagIds)
->addSelect('COUNT(tag) AS HIDDEN numberOfTags')
->groupBy('news')
->having('numberOfTags = :numberOfTags')
->setParameter('numberOfTags', count($tags))
;
}
return $qb
->getQuery()
->getResult()
;
}
假设我有“新闻”实体,它具有多对多“标签”关系
class News
{
/**
* @ORM\ManyToMany(targetEntity="App\Domain\Entity\Vocabulary\Tag")
*/
private Collection $tags;
}
我有这样的疑问:
public function getList(
array $tags = null,
): Query {
if (null !== $tags) {
$qb->andWhere('nt.id IN (:tags)');
$qb->setParameter('tags', $tags);
}
}
问题是当我传递 ["Tag1", "Tag2"] 时,selects 新闻具有第一个标签或第二个标签,但不能同时具有这两个标签。我如何将查询重写为同时具有两个标签的 select 新闻?
首先要注意的一些事情:
对于学说注释,可以使用 ::class
-常量:
use App\Domain\Entity\Vocabulary\Tag;
class News
{
/**
* @ORM\ManyToMany(targetEntity=Tag::class)
*/
private Collection $tags;
}
如果 $tags
数组为空,则将抛出异常,因为空值集无效 SQL,至少在 mysql:
nt.id IN () # invalid!
现在问题来了:
使用SQL聚合函数COUNT
和GROUP BY
我们可以统计所有新闻的标签数量。连同您对允许标签的条件,每个新闻的标签数量必须等于标签数组中的标签数量:
/**
* @var EntityManagerInterface
*/
private $manager;
...
/**
* @param list<Tag> $tags - Optional tag filter // "list" is a vimeo psalm annotation.
*
* @return list<News>
*/
public function getNews(array $tags = []): array
{
$qb = $this->manager
->createQueryBuilder()
->from(News::class, 'news')
->select('news')
;
if(!empty($tags)) {
$tagIds = array_unique(
array_map(static function(Tag $tag): int {
return $tag->getId();
}) // For performance reasons, give doctrine ids instead of objects.
); // Make sure duplicate tags are handled.
$qb
->join('news.tags', 'tag')
->where('tag IN (:tags)')
->setParameter('tags', $tagIds)
->addSelect('COUNT(tag) AS HIDDEN numberOfTags')
->groupBy('news')
->having('numberOfTags = :numberOfTags')
->setParameter('numberOfTags', count($tags))
;
}
return $qb
->getQuery()
->getResult()
;
}