Symfony4中Doctrine查询构造器LEFT JOIN多条件筛选问题
解决Symfony 4 Doctrine查询构造器筛选未关联特定食谱的标签问题
嘿,我懂你现在卡在哪了——你的原生SQL逻辑完全正确,但用Doctrine QueryBuilder实现时没拿到预期结果,这是因为Doctrine处理多对多关联的方式和原生SQL的左连接逻辑有细微差异。
首先先确认下你的实体映射没问题:Tag实体应该有一个指向Recipe的多对多关联,关联表是tag_recipe,大概是这样的:
// src/Entity/Tag.php /** * @ORM\ManyToMany(targetEntity=Recipe::class, inversedBy="tags") * @ORM\JoinTable(name="tag_recipe") */ private $recipes;
接下来分析你的问题:你原代码里的左连接加WITH条件,其实只筛选了关联到指定食谱的那一条关联记录,但如果标签还关联了其他食谱,r就不会为null——而你要的是完全没关联这个特定食谱的标签,不管它有没有其他关联。所以原代码的where('r is NULL')只会返回没有任何关联的标签,这就是为什么你得到空列表或者不对的结果。
下面给你几个可行的解决方案,按推荐程度排序:
方案一:用DQL的NOT MEMBER OF语法(最简洁)
Doctrine专门为这种关联判断提供了语法,直接就能表达“某个实体不在关联集合里”的逻辑:
'query_builder' => function(EntityRepository $er) { return $er->createQueryBuilder('t') ->where(':recipe NOT MEMBER OF t.recipes') ->setParameter('recipe', $this->recipe); }
这个写法既简洁又符合ORM的风格,完全匹配你的需求——筛选出所有关联集合里不包含指定$this->recipe的标签。
方案二:用分组统计的方式(贴近你的原生SQL思路)
如果你更习惯用连接的方式,可以调整成通过分组统计关联指定食谱的数量,筛选数量为0的标签:
'query_builder' => function(EntityRepository $er) { return $er->createQueryBuilder('t') ->leftJoin('t.recipes', 'r', \Doctrine\ORM\Query\Expr\Join::WITH, 'r.id = :recipe') ->groupBy('t.id') ->having('COUNT(r.id) = 0') ->setParameter('recipe', $this->recipe); }
这里通过GROUP BY标签ID,然后统计关联到指定食谱的记录数,等于0就说明这个标签没关联该食谱。
方案三:用NOT EXISTS子查询(逻辑最清晰)
如果你喜欢逻辑更直白的写法,子查询的方式也很直观:
'query_builder' => function(EntityRepository $er) { // 子查询:找到所有关联了指定食谱的标签ID $subQuery = $er->createQueryBuilder('sub_t') ->select('sub_t.id') ->innerJoin('sub_t.recipes', 'sub_r') ->where('sub_r.id = :recipe'); return $er->createQueryBuilder('t') ->where($er->expr()->notIn('t.id', $subQuery->getDQL())) ->setParameter('recipe', $this->recipe); }
这个逻辑就是:先找出所有关联了目标食谱的标签,然后筛选出不在这个列表里的标签,完全对应你的需求。
你可以优先试试方案一,应该直接就能解决问题,语法也最简洁。
内容的提问来源于stack exchange,提问作者Fanny Rivey
相关产品推荐
相关产品推荐

