我与我的一个模特有关系:
/**
* @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做到这一点?
答案 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中重写一个学说类解决了这个问题,对我来说看起来像这样:
<?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;
}
}