学说preRemove OneToMany

时间:2012-01-16 00:45:56

标签: doctrine-orm

在我的模特中我有

Terrain.php

/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{

 /**
  * @var lots
  *
  * @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain")
  */
  private $lotes;

  /**
   * @ORM\PreRemove
   */
  public function deleteAllLots()
  {
    $lots = $this->getLots();

    foreach ($lots as $lot) {
        $this->lots->removeElement($lot);
    }
  }

}

Lot.php

/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Lot
{

 /**
  * @var Terrain
  *
  * @ORM\ManyToOne(targetEntity="Terrain", inversedBy="lots")
  * @ORM\JoinColumns({
  *   @ORM\JoinColumn(name="terrain_id", referencedColumnName="id")
  * })
  */
  var $terrain

}

我正在尝试删除地形,但之前我删除了与之相关的所有地段。当我尝试删除地形时,我收到错误SQLSTATE[23000]: Integrity constraint violation

3 个答案:

答案 0 :(得分:9)

您在删除时仍然有lotsterrain的引用,这就是您拥有Integrity constrait violation的原因。

试试这个:

/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{
    /**
    * @var lots
    *
    * @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain")
    */
    private $lotes;

    /**
    * @ORM\PreRemove
    */
    public function deleteAllLots()
    {
        $lots = $this->getLots();

        foreach ($lots as $lot) {
            $this->lots->removeElement($lot);
            $lot->terrain = null; //$lot->setTerrain(null); will be better, try to add getters and setters
        }
    }
}

如果要从数据库中删除lots,则应使用cascade关系(One-To-Many reference on doctrine-project.com)的OneToMany属性,如下所示:

/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{
    /**
    * @var lots
    *
    * @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain", cascade={"remove"})
    */
    private $lotes;
}

Terrain删除后,将删除所有批次。

答案 1 :(得分:1)

发布的第一个代码here也不会删除第二个表中的数据,它只是使引用为空,如果你只依赖一个实体,通常不应该为null(你不应该有任何实体)在该表中没有任何引用的旧行,因为在大多数情况下,您无法使用它们,它只会使该表中包含您不需要的内容并且使您的索引变得棘手)。

来自doctrine 2 docs

  

生命周期回调是触发事件时调用的实体类的方法。它们绝对不接受任何参数,并且专门设计为允许实体类状态内的更改。   生命周期事件监听器是具有特定回调方法的类,它们接收某种类型的EventArgs实例,该实例可以访问实体,EntityManager或其他相关数据。

生命周期回调仅用于更改该实体,而不是其所引用的其他实体。 您必须使用Lifecycle Event Listener而不是Lifecycle Callback。 使用Lifecycle Event Listener,您可以访问实体管理器并删除这些实体。 但是正确的方法是在引用上使用delete cascade,这样,doctrine可以忽略引用的对象(不必从数据库中读取它们只是为了以后删除它们1)并让数据库处理它们。 你应该改变这一行:

@ORM\JoinColumn(name="terrain_id", referencedColumnName="id")

到这个

@ORM\JoinColumn(name="terrain_id", referencedColumnName="id", onDelete="cascade")

您可以在JoinColumn注释here找到文档。

您可以按照建议的hereOneToMany注释使用cascade =“remove”参数,但速度要慢得多。

答案 2 :(得分:0)

为什么不使用PostRemove而不是PreRemove?此方法允许您在批次之后删除Terrain而不会违反数据库完整性。删除Lots后,现在您可以安全地删除相关的Terrain。

/**
 * @ORM\PostRemove
 */
public function deleteTerrain(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
    $em = $args->getEntityManager();

    $terrain = $this->getTerrain();
    $em->remove($terrain);
    $em->flush();
}