禁用Doctrine外键约束

时间:2016-09-06 22:59:34

标签: php mysql symfony doctrine-orm

我与我的一个模特有关系:

/**
* @ORM\ManyToOne(targetEntity="Page", cascade="persist")
* @ORM\JoinColumn(name="page_id", referencedColumnName="id")
*/
private $parentPage;

当我删除父页面时,我收到此错误:

Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails

基本上我的模型是页面和页面修订版。当我删除页面我不想删除修订版时。我还想在页面修订版上保留page_id(即不将其设置为null)。

我如何使用Doctrine做到这一点?

5 个答案:

答案 0 :(得分:12)

通过定义,如果不将密钥设置为null(onDelete="SET NULL")或级联删除操作(There are two options - ORM,则无法删除外键所指向的记录级别:cascade={"remove"} |数据库级别:onDelete="CASCADE")。
setting a default value of a still existing record的替代方案,但你必须手动完成,我不认为Doctrine支持这个out-of-the-box(如果我错了请纠正我,但在这种情况下设置无论如何都不需要默认值。

这种严格性反映了具有外键约束的概念;喜欢@Théo说:

  

FK是为了确保数据的一致性

软删除(已经提到)是一种解决方案,但您还可以执行的是在removed_page_id中删除page_id之前与preRemove同步的列。Revision事件处理程序(生命周期回调)。这些信息是否具有任何价值我想知道,但我猜你有一些用处,否则你不会问这个问题。

我绝对没有声称这是一个好习惯,但它至少是你可以用于边缘情况的东西。所以有以下几点:

/** * @ORM\ManyToOne(targetEntity="Page", cascade="persist") * @ORM\JoinColumn(name="page_id", referencedColumnName="id", onDelete="SET NULL") */ private $parentPage; /** * @var int * @ORM\Column(type="integer", name="removed_page_id", nullable=true) */ protected $removedPageId;

Page

然后在/** * @ORM\PreRemove */ public function preRemovePageHandler(LifecycleEventArgs $args) { $entityManager = $args->getEntityManager(); $page = $args->getEntity(); $revisions = $page->getRevisions(); foreach($revisions as $revision){ $revision->setRemovedPageId($page->getId()); $entityManager->persist($revision); } $entityManager->flush(); }

$removedPageId

或者,您当然可以在构建Revision期间设置正确的db.test.update( {id: 1}, {"$pullAll" : {"comments" : [{ "cid" : "3"},{ "cid" : "2"}]}}) 值,然后您甚至不需要在删除时执行生命周期回调。

答案 1 :(得分:2)

您明确要求数据不一致,但我很确定您真的不想要这样。我无法想到这种情况可以防御的情况。这是一种不好的做法,肯定会引起问题。例如:$revision->getPage()的预期结果是什么?

有一个非常简单而优雅的解决方案:softdeletable。它基本上为您的实体添加了一个属性(换句话说:向您的表添加列),名为deletedAt,以存储if(或更好:何时)该实体被删除。因此,如果该属性为null,则该实体不会被删除。

您唯一需要做的就是添加this bundle,向您的实体添加特征(Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity)并更新您的数据库。实现起来非常简单:这个包将为您完成工作。阅读the documentation以了解此扩展程序。

或者,您可以添加'已启用'布尔属性或状态字段(例如'已发布','草稿','已删除')。

答案 2 :(得分:1)

您可以禁用特定型号的外键导出:

User:
  attributes:
    export: tables
  columns:

现在它只会导出表定义而不导出任何外键。您可以使用:none,tables,constraints,plugins或all。

答案 3 :(得分:1)

  

当我删除页面时,我不想删除修订版。我还想在页面修订版上保留page_id(即不将其设置为null)。

我认为你已经得到了答案:学说不会这样做,仅仅因为它与外键的概念不相符。 FK的原则是确保数据的一致性,因此如果您有FK,它必须引用现有的ID。在删除时,某些数据库引擎(例如InnoDB for MySQL)允许您将FK放到NULL(假设您确实使FK列可以为空)。但是提到不存在的ID是不可行的,或者它不是FK。

如果你真的想这样做,不要在这个特定情况下使用Doctrine,它不会阻止你在你的代码库中的其他地方使用Doctrine。另一个解决方案是手动删除FK约束或在查询之前使用DB语句跳过FK检查。

答案 4 :(得分:1)

我通过在symfony 4.3中重写一个学说类解决了这个问题,对我来说看起来像这样:

enter image description here

enter image description here

enter image description here

<?php declare(strict_types=1);

namespace App\DBAL;

use Doctrine\DBAL\Platforms\MySQLPlatform;

/**
 * Class MySQLPlatformService
 * @package App\DBAL
 */
class MySQLPlatformService extends MySQLPlatform
{
    /**
     * Disabling the creation of foreign keys in the database (partitioning is used)
     * @return false
     */
    public function supportsForeignKeyConstraints(): bool
    {
        return false;
    }

    /**
     * Disabling the creation of foreign keys in the database (partitioning is used)
     * @return false
     */
    public function supportsForeignKeyOnUpdate(): bool
    {
        return false;
    }
}