Symfony5与Doctrine查询构建:获取无本地化内容的Excel记录
Symfony5 + Doctrine 查询实现:获取关联工作表无LocalizedWorksheetContent的Excel数据
需求说明
需要查询满足以下条件的Excel数据:其关联的Worksheet实体未包含任何LocalizedWorksheetContent记录。
实体结构
Excel实体
class Excel extends Asset { /** * @var Collection<Worksheet> */ #[ORM\JoinTable(name: 'excel_worksheet_reference')] #[ORM\JoinColumn(name: 'excel_id', referencedColumnName: 'id')] #[ORM\InverseJoinColumn(name: 'worksheet_id', referencedColumnName: 'id')] #[ORM\ManyToMany(targetEntity: Worksheet::class, cascade: ['persist', 'remove'])] #[ORM\OrderBy(['sorting' => 'ASC', 'name' => 'ASC'])] private Collection $worksheets; }
Worksheet实体
class Worksheet { /** @var Collection<LocalizedWorksheetContent> */ #[ORM\OneToMany(mappedBy: 'worksheet', targetEntity: LocalizedWorksheetContent::class, cascade: ['persist', 'remove'])] private Collection $localizedWorksheetContents; }
LocalizedWorksheetContent实体
class LocalizedWorksheetContent { #[ORM\ManyToOne(targetEntity: Worksheet::class, inversedBy: 'localizedWorksheetContents')] #[ORM\JoinColumn(name: 'worksheet_id', referencedColumnName: 'id', onDelete: 'CASCADE')] private ?Worksheet $worksheet; }
当前错误代码
原查询返回所有记录,核心问题是exists子句的关联条件错误,逻辑判断不准确:
/** * @return string[] */ public function findProcessedExcelsWithoutLocalizedContent(): array { $qb = $this->_em->createQueryBuilder(); $qb ->select('DISTINCT e.id') ->from(Excel::class, 'e') ->leftJoin('e.worksheets', 'w') ->leftJoin('w.localizedWorksheetContents', 'wt') ->where( $qb->expr()->andX( $qb->expr()->isNotNull('w.id'), $qb->expr()->not( $qb->expr()->exists( $this->_em->createQueryBuilder() ->select('1') ->from(LocalizedWorksheetContent::class, 'wt_sub') ->where('wt_sub.worksheet = w.id') ) ) ) ); $uids = $qb->getQuery()->getArrayResult(); return array_map(fn (array $row) => $row['id'], $uids); }
解决方案
方案1:LEFT JOIN + IS NULL 筛选
通过左连接关联内容表,直接筛选无匹配内容的Worksheet关联的Excel:
/** * @return string[] */ public function findProcessedExcelsWithoutLocalizedContent(): array { $qb = $this->_em->createQueryBuilder(); $qb ->select('DISTINCT e.id') ->from(Excel::class, 'e') ->innerJoin('e.worksheets', 'w') // 确保Excel有关联的Worksheet ->leftJoin('w.localizedWorksheetContents', 'wt') ->where($qb->expr()->isNull('wt.id')); // 筛选Worksheet无对应内容的情况 $uids = $qb->getQuery()->getArrayResult(); return array_map(fn (array $row) => $row['id'], $uids); }
方案2:修正NOT EXISTS子句逻辑
修正关联实体的条件写法(直接关联实体而非ID),确保子查询逻辑正确:
/** * @return string[] */ public function findProcessedExcelsWithoutLocalizedContent(): array { $qb = $this->_em->createQueryBuilder(); $qb ->select('DISTINCT e.id') ->from(Excel::class, 'e') ->innerJoin('e.worksheets', 'w') ->where( $qb->expr()->not( $qb->expr()->exists( $this->_em->createQueryBuilder() ->select('1') ->from(LocalizedWorksheetContent::class, 'wt_sub') ->where('wt_sub.worksheet = w') // 直接关联实体别名,Doctrine自动处理ID映射 ) ) ); $uids = $qb->getQuery()->getArrayResult(); return array_map(fn (array $row) => $row['id'], $uids); }
方案3:GROUP BY + HAVING 统计内容总数(适配所有关联Worksheet无内容的场景)
如果需求是Excel的所有关联Worksheet都没有LocalizedWorksheetContent,可以用分组统计实现:
/** * @return string[] */ public function findProcessedExcelsWithoutLocalizedContent(): array { $qb = $this->_em->createQueryBuilder(); $qb ->select('e.id') ->from(Excel::class, 'e') ->leftJoin('e.worksheets', 'w') ->leftJoin('w.localizedWorksheetContents', 'wt') ->groupBy('e.id') ->having($qb->expr()->eq($qb->expr()->count('wt.id'), 0)); // 所有关联Worksheet的内容总数为0 $uids = $qb->getQuery()->getArrayResult(); return array_map(fn (array $row) => $row['id'], $uids); }
关键问题说明
- 原查询中
wt_sub.worksheet = w.id是错误写法,Doctrine查询构建器中关联实体字段时,直接使用实体别名即可(如wt_sub.worksheet = w),底层会自动处理ID关联逻辑。 - 如果不需要限制Excel必须有Worksheet,可将
innerJoin改为leftJoin,同时补充$qb->expr()->isNotNull('w.id')的条件。
内容的提问来源于stack exchange,提问作者Chanyss921
相关产品推荐
相关产品推荐

