如何禁用多对多关系的级联删除?

How to disable cascade deletion for many-to-many relations?

对于多对多关系(例如组和用户),只要删除其中一个实体,联合 table 中的行就会自动删除,就像为 cascade remove 属性设置一样的关系。所以基本上我想删除它 IF ONLY IT IS EMPTY。所以解决方案必须保证没有关系被删除(就像 FK 约束保证它一样)。

是否可以默认不这样做并在违反外键约束时抛出异常?

PS:删除前检查不是解决方案,因为它容易出现竞争条件。

PPS: 映射定义很琐碎,为了完整起见,我在这里 post 它们(即使它们没有带来任何有用的东西)

PPPS: onDelete: cascade 也不是解决方案:它在数据库级别创建相应的 ON DELETE CASCADE

PPPPS: ON DELETE RESTRICT 不能使用 因为学说将从联合 table.[=15= 中删除所有引用]

担任角色:

manyToMany:
    users:
        targetEntity: UserAccount
        mappedBy: roles

在用户中:

manyToMany:
    roles:
        targetEntity: Role
        joinTable:
            name: user_role
            joinColumns:
                user_id:
                    referencedColumnName: id
            inverseJoinColumns:
                role_id:
                    referencedColumnName: id

这个答案可以被视为解决方法many-to-many关联可以替换为3个参与类之间的one-to-many/many-to-one关联,因为Doctrine默认没有与one-to-many的级联删除。

几个小时前我遇到了同样的问题: 我在 UserGroup 实体之间有一个简单的 ManyToMany 关联。

如果有一些相关的 users,我不希望 Doctrine 删除一个 Group 实体,而我希望 Doctrine 删除一个 User,即使它在某个组中。

为了完成这个,在 Group 实体中我有这行代码:

/**
 * Group
 *
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 * 
 */

class Group
{
...
    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * 
     * @ORM\ManyToMany(targetEntity="User", mappedBy="groups")
     * 
     */
    private $users;

...

    /**
     * @ORM\PreRemove()
     */
    public function preRemove() {
    if ($this->users->count()>0) {
        throw new \AppBundle\Utils\Exception\FKConstraintViolationException();
    }
    }

}

这是 FKConstraintViolationException class

namespace AppBundle\Utils\Exception;

class FKConstraintViolationException extends \Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException {
    public function __construct() {
            $message='Self generated exception';
            $driverException=new \Doctrine\DBAL\Driver\OCI8\OCI8Exception('Self generated exception.'); //or whatever driver you use if you have another db
            parent::__construct($message, $driverException);
    }
}

在控制器中,我将 remove() 放在 try/catch 块中

try {
    $em->remove($group);
    $em->flush();
    $this->addFlash('success', 'deleted_successfully');
} catch (\Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException $e) {
    $this->addFlash('error', 'cannotdelete_related_entities');
}

这是文档 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events

也许最好在 preFlushonFlush 事件上创建一个侦听器,因为您实际上可以在对象上调用 remove() 而无需刷新,并且您会获得异常。