You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
}

关键问题说明

  1. 原查询中wt_sub.worksheet = w.id是错误写法,Doctrine查询构建器中关联实体字段时,直接使用实体别名即可(如wt_sub.worksheet = w),底层会自动处理ID关联逻辑。
  2. 如果不需要限制Excel必须有Worksheet,可将innerJoin改为leftJoin,同时补充$qb->expr()->isNotNull('w.id')的条件。

内容的提问来源于stack exchange,提问作者Chanyss921

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:19:52