【发布时间】:2015-03-17 20:44:59
【问题描述】:
上下文
我的目标是使用来自同一类但不受 Doctrine 管理的对象的数据对托管实体执行更新。
如果可以在替换属性时执行“简单更新”,这会很酷,但事实上,如果我清理 ArrayCollection,旧数据似乎不会被删除(即使我清理所有引用ArrayCollection 或 orphanRemoval 的元素的小提琴设置为 true)。
但是让我们输入一个具体的例子。我有 this entity 有很多 OneToOne / OneToMany 关系来代表 fiddle。我可以使用 Symfony2 命令导入小提琴样本(以前从另一个环境导出为 json)。
如果示例已经存在,我该如何正确更新它?
坏主意:做 DELETE + INSERT
我使用以下代码(简化)构建我的实体:
$fiddle = new Fiddle();
$fiddle->setHash($this->get($json, 'hash'));
$fiddle->setRevision($this->get($json, 'revision'));
$context = $fiddle->getContext();
$context->setFormat($this->get($json, 'context', 'format'));
$context->setContent($this->get($json, 'context', 'content'));
$fiddle->clearTemplates();
$jsonTemplates = $this->get($json, 'templates') ? : array ();
foreach ($jsonTemplates as $jsonTemplate)
{
$template = new FiddleTemplate();
$template->setFilename($this->get($jsonTemplate, 'filename'));
$template->setContent($this->get($jsonTemplate, 'content'));
$template->setIsMain($this->get($jsonTemplate, 'is-main'));
$fiddle->addTemplate($template);
}
// ...
如果我的实体已经存在,我现在可以在删除它后保留它:
$check = $this
->getContainer()
->get('doctrine')
->getRepository('FuzAppBundle:Fiddle')
->getFiddle($fiddle->getHash(), $fiddle->getRevision());
if (!is_null($check->getId()))
{
$em->remove($check);
$em->flush();
}
$em->persist($fiddle);
$em->flush();
但是如果样本已经存在,这将创建一个 DELETE + INSERT 而不是 UPDATE。这很奇怪,因为用户可以为小提琴添加书签,并且关系是由 id 建立的。
丑陋的想法:对主要实体和 OneToOne 关系执行 UPDATE,并在 OneToMany 关系上执行 DELETE + INSERT
我先得到我的小提琴,如果它已经存在,我清理它并用新数据填充它... 代码运行良好,但真的很丑,你可以检查它here .
作为示例,查看tags 属性:由于标签可能已被删除/更改,我应该正确设置新标签,将旧标签替换为新标签。
// remove the old tags
foreach ($fiddle->getTags() as $tag)
{
if (\Doctrine\ORM\UnitOfWork::STATE_MANAGED === $em->getUnitOfWork()->getEntityState($tag))
{
$em->remove($tag);
$em->flush();
}
}
// set the new tags
$tags = new ArrayCollection();
$jsonTags = $this->getFromArray($json, 'tags');
foreach ($jsonTags as $jsonTag)
{
$tag = new FiddleTag();
$tag->setTag($jsonTag);
$tags->add($tag);
}
$fiddle->setTags($tags);
由于使用 fiddle 的 id 引用标签,我可以使用 ->remove,即使这很丑陋。这里没问题,但如果 id 是自动生成的,那么肯定有更好的解决方案。
我也尝试简单地将旧小提琴的 id 设置为新的并合并,但这导致了以下异常:
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Undefined index: 00000000125168f2000000014b64e87f
赏金?
不仅仅是一个简单的“导入功能”,我还想使用这种更新样式将表单绑定到非托管实体,并仅在需要时更新现有实体。所以我的目标是让一些通用的东西适用于所有类型的实体。
但我当然不期望整个代码。处理托管 ArrayCollection 的更新的良好做法,以及在编写此功能之前我应该考虑的一些提示/警告就足够了。
【问题讨论】:
-
我想我对发生的事情感到困惑。为什么不直接获取您的实体,进行更新,然后刷新?你为什么要删除/保留?
-
我无法轻松进行更新,因为有很多 OneToOne / OneToMany :以包含 ArrayCollection 的
tags属性为例:也许删除了一些标签并添加了一些其他标签,我需要使用复杂(和丑陋)的代码来进行正确的更新。我使用了更新 here 但正如你所看到的,这是一个用于正确更新对象的巨大代码 - 如果 Doctrine 有它,我正在寻找更自动/更简单的东西。 -
我只是希望我能更好地理解您的具体问题,或者您通过自动刷新托管实体等的含义。
-
当我将托管实体绑定到已发布的表单 (
$form->handleRequest($fiddle);)` 并且表单正确填写($form->isValid()返回 true)时,我需要进行一些其他检查以查看是否当前用户能够保存它。如果他被允许保存,没问题;但是如果他没有被允许保存,我应该分离实体以避免它被自动更新。我在 3 天前发现了这个令人难以置信的自动更新,在我完全理解发生了什么之前,我永远不会再使用托管实体来处理表单。 -
您不应该在表单检查是否有效之前进行安全检查吗?想要检查
@Security注释?
标签: php symfony doctrine-orm entity