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

时间:2015-04-20 00:39:13

标签: php database symfony doctrine-orm doctrine

对于多对多关系(比如组和用户),只要删除了任一实体,就会自动删除联合表中的行,就像为关系设置了级联删除属性一样。所以基本上我想删除它如果它只是空的。所以解决方案必须保证不会丢弃任何关系(就像FK约束保证它一样)。

默认情况下是否可以不执行此操作并在外键约束违反时抛出异常?

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

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

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

PPPPS:ON DELETE RESTRICT 无法使用,因为doctrine将删除联合表中的所有引用。

角色:

manyToMany:
    users:
        targetEntity: UserAccount
        mappedBy: roles

在用户中:

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

2 个答案:

答案 0 :(得分:2)

This answer can be considered as a workaround. The many-to-many association can be replaced by one-to-many/many-to-one associations between the 3 participating classes, because Doctrine has no cascade deleting with one-to-many by default.

one-to-many/many-to-one

答案 1 :(得分:0)

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

如果有一些相关Group,我不希望Doctrine删除users实体,而我希望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类

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()而不会刷新,你会得到异常。