如何在Doctrine中通过ID判空优雅实现左连接?排查关联查询代码问题
在Doctrine中优雅实现带条件的左连接
首先得指出你当前代码里的两个关键问题:
- 连续调用两次
where()会导致第一个条件被覆盖,Doctrine的QueryBuilder中,where()是设置基础条件,后续的条件需要用andWhere()来追加 - 把
n.id = :id放在where()里会破坏左连接的效果——左连接后那些没有关联NewsletterItem的UserGroup对应的n.id是NULL,这个条件会直接把这些记录过滤掉,相当于变成了内连接
修正后的代码实现
public function loadUserGroupsOfNewsletter($ids, $newsletterID) { $qb = $this->getEntityManager()->createQueryBuilder(); return $qb->select('ug, gon') ->from('MyNameSpace:UserGroup', 'ug') // 左连接时直接指定关联条件,确保只关联目标newsletter的GroupsOfNewsletter ->leftJoin('ug.groupsOfNewsletter', 'gon', 'WITH', 'gon.newsletterItem = :newsletterId') ->where($qb->expr()->in('ug.id', $ids)) ->setParameter('newsletterId', $newsletterID) ->getQuery() ->getResult(); }
关键细节说明
用
andWhere()追加条件(如果需要多个基础条件)
如果你后续还要加其他条件,别直接用where(),改用andWhere(),比如:->where($qb->expr()->in('ug.id', $ids)) ->andWhere('ug.isActive = true')这样多个条件会用
AND组合,不会互相覆盖。左连接的条件要放在
WITH子句中
在Doctrine的leftJoin()方法里,第三个参数就是WITH子句的位置,把关联newsletterItem的条件放在这里,就能保证:- 所有匹配
ug.id IN (:ids)的UserGroup都会被返回 - 只有关联了指定
newsletterID的groupsOfNewsletter才会被关联进来,没有关联的gon字段会是NULL
- 所有匹配
如何判断是否关联了目标Newsletter
拿到结果后,你可以直接判断$userGroup->getGroupsOfNewsletter()是否为空,或者在查询里用CASE表达式标记:->select('ug, gon, CASE WHEN gon.id IS NOT NULL THEN 1 ELSE 0 END AS hasAssociated')
这样就能优雅地实现你想要的左连接效果,同时保留所有符合ID条件的UserGroup,不管它们有没有关联目标Newsletter。
内容的提问来源于stack exchange,提问作者Lajos Arpad
相关产品推荐
相关产品推荐

