Doctrine QueryBuilder 删除连表数据

21

我正在尝试使用Doctrine QueryBuilder执行以下SQL查询:

DELETE php FROM product_hole_pattern php
INNER JOIN hole_pattern hp ON php.hole_pattern_id = hp.id
INNER JOIN hole_pattern_type hpt ON hp.hole_pattern_type_id = hpt.id
WHERE php.product_id = 4 AND hpt.slug='universal';

我有这个

$qb = $this->entityManager->createQueryBuilder();
$query = $qb->delete('\SANUS\Entity\ProductHolePattern', 'php')
  ->innerJoin('php.holePattern', 'hp')
  ->innerJoin('hp.holePatternType', 'hpt')
  ->where('hpt.slug = :slug AND php.product=:product')
  ->setParameter('slug','universal')
  ->setParameter('product',$this->id)
  ->getQuery();

但我得到:

[Semantical Error] line 0, col 50 near 'hpt.slug = :slug': Error: 'hpt' is not defined.

附带错误信息的DQL为:

DELETE \SANUS\Entity\ProductHolePattern php 
WHERE hpt.slug = :slug AND php.product=:product

所以这些连接似乎被完全省略了。

4个回答

22

使用IN条件查询可能比迭代更好。

$ids = $this->createQueryBuilder('product')
->join('..your joins..')
->where('..your wheres..')
->select('product.id')
->getQuery()->getResult();

$this->createQueryBuilder('product')
    ->where('product.id in (:ids)')
    ->setParameter('ids', $ids)
    ->delete()
    ->getQuery()
    ->execute();
  • 好处:运行更快,无需迭代
  • 缺点:无法挂钩preRemove

至于热烈的“将其放在哪里”的辩论,如果您喜欢,敢将其放在控制器中。这完全取决于您。但是,如果您将代码放入专用的Doctrine存储库类中,则将来可能对您更有用。这很容易做到,并且易于更改/维护。


这是一个相当老的问题,我只能勉强记得当时发布了它。我同意这种方法更快。我真的不明白将代码放入控制器的想法来自何处。位置并不是我最初提出的问题的一部分。我将其切换为被接受的答案。 - Chris Hanson

15

看起来DQL不支持这种删除语句。Doctrine文档中的BNF表示,delete_statement必须采用以下形式:

delete_clause [where_clause]

其中delete_clause的定义如下:

"DELETE" "FROM" abstract_schema_name [["AS"] identification_variable]

我可以提供模式和where子句,但没有连接。


4
实现这个目标的方法可能是先使用连接查询来查询您要删除的实体:
$qb = $this->entityManager->createQueryBuilder();
$query = $qb->select('\SANUS\Entity\ProductHolePattern', 'php')
  ->innerJoin('php.holePattern', 'hp')
  ->innerJoin('hp.holePatternType', 'hpt')
  ->where('hpt.slug = :slug AND php.product=:product')
  ->setParameter('slug','universal')
  ->setParameter('product',$this->id)
  ->getQuery();
$results = $query->execute();

然后删除你在结果中找到的实体:

foreach ($results as $result) {
  $this->entityManager->remove($result);
}

一定要调用

$this->entityManager->flush();

将以下代码放置在您的应用程序中适当的位置(通常是控制器)。


6
只有两点需要提及。1. 这可能非常昂贵。2. 切勿将此类逻辑放入控制器中! - Lukas Lukac
4
第三个问题 - 这容易产生竞态条件。 - Coloured Panda
4
构建删除查询的整个目的是,您不必使您要删除并循环的所有对象变得活动,并因此可以节省大量成本,具体取决于您要删除多少对象... - axelvnk

-6

在Symfony2中,请尝试:

foreach ($results as $result) {
  $em->remove($result);
}

$em->flush();

就这些了。


在需要更精细的删除(例如原始问题)的情况下,这个默认的$em->remove($entity) 方法不起作用。 - Chadwick Meyer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接