当通过onDelete =“CASCADE”删除实体时,Doctrine cascade = {remove}不起作用

时间:2018-02-18 14:16:46

标签: php doctrine-orm

我遇到了使用Doctrine 2的实体之间级联关系的问题。

我有一个与父事件相关的Media实体:

class Media
{

    /**
     * @ORM\OneToOne(targetEntity="Event", mappedBy="media")
     */
    private $event;

    public function getEvent()
    {
        return $this->event;
    }

    public function setEvent(Event $event)
    {
        $this->event = $event;
    }

}

每个Event与媒体相关(双向),也与Import实体相关。

class Event
{

    /**
     * @ORM\JoinColumn(name="media", referencedColumnName="id", onDelete="SET NULL")
     * @ORM\OneToOne(targetEntity="Media", inversedBy="event", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    private $media;

    public function getMedia()
    {
        return $this->media;
    }

    public function setMedia(Media $media = null)
    {
        $this->media = $media;
    }


    /**
     * @ORM\JoinColumn(name="import", referencedColumnName="id", nullable=true, onDelete="CASCADE")
     * @ORM\ManyToOne(targetEntity="Import")
     */
    private $import;

    public function getImport()
    {
        return $this->import;
    }

    public function setImport(Import $import = null)
    {
        $this->import = $import;
    }

}

预期的行为如下:

  • Event实体在删除父Import时会自动删除(与onDelete="CASCADE"的ManyToOne关系)。
  • Event实体还包含与Media实体的引用(OneToOne关系),在删除事件时必须删除该引用。

两者都运作良好:

  • 如果删除导入,则会删除所有相关事件。
  • 如果我删除了一个事件,相关的媒体将被删除。

但是,如果删除导入,虽然事件已删除,但与已删除事件相关的介质不是

关于可能发生的事情的任何想法?谢谢!

1 个答案:

答案 0 :(得分:4)

您描述的问题是预期的行为。 onDelete="CASCADE"选项强制执行数据库内部执行的行为,而选项cascade={"remove"}通过Doctrine处理,并应用于在内存中执行的对象in the documentation

  

级联操作在内存中执行。这意味着当即将执行级联操作时,集合和相关实体被提取到存储器中(即使它们被标记为惰性)。此方法允许为每个操作执行实体生命周期事件。

这两种方法都是有效的,但它们暗示了in this section所讨论的不同内容。

您的设置实际发生的情况是,您希望混合使用onDelete="CASCADE"cascade={"remove"}在您的方案中一起运行,但由于它们的性质,它们不能。

实际上,由于Importcascade={"remove"}之间没有反面,因此当您删除Import时:

  • 在与DELETE
  • 对应的表格中的数据库中执行Import操作
  • 感谢用于Event的表格中的外键,与Import相关的事件将直接在您的数据库中删除

从那里开始,没有其他内容被执行,因为用于Media的表没有任何引用Event表的外键(因为它在关联的反面)。

要做到这一点,你可以做两件事:

反转媒体/事件关联并在onDelete="CASCADE"

上添加Media Media.php

/**
 * @ORM\OneToOne(targetEntity="Event", inversedBy="media")
 * @ORM\JoinColumn(onDelete="CASCADE")
 */
private $event;
Event.php

/**
 * @ORM\OneToOne(targetEntity="Media", mappedBy="event")
 */
private $media;

使用这种方法,删除数据库中的Import条目必然会删除相关的Event条目,并且通过相同的机制,相关的Media将被删除(所有这些都直接由你的数据库,Doctrine刚刚在DELETE表上发布了一个Import操作。

Import中添加反面并使用cascade={"remove"}

如果您使用OneToManyImport中添加反向cascade={"remove"},则使用实体管理器执行的删除操作将级联到相关的Event实体,这些实体也会级联删除对任何关联的Media进行操作。

如果您希望为这些实体执行生命周期事件,这将非常有用。

这并不意味着您必须在两种方法之间进行选择。文档说明如下:

  

但是你应该知道,使用策略1(CASCADE = REMOVE)完全绕过任何外键onDelete = CASCADE选项,因为Doctrine将显式地获取并删除所有相关实体。

话虽如此,如果您希望数据库保持正常状态,那么除了onDelete=CASCADE之外还有cascade={"remove"}是有意义的。例如,如果直接执行DELETE个查询(不使用实体管理器),则在没有onDelete=CASCADE的情况下不会删除相关条目,并且您的RDBMS很可能会抱怨无效的外键约束。