背景
我的目标是使用同一类的非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建立的。
丑陋的想法:对主实体和一对一关系进行更新,对一对多关系进行删除+插入
我首先获取我的小部件,如果它已经存在,我就清理它并用新数据填充它...代码运行良好,但非常丑陋,您可以在这里检查它。
作为示例,请检查
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是自动生成的,则必须有更好的解决方案。我还尝试了将旧的fiddle id设置为新的id并进行合并,但导致了以下异常:
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Undefined index: 00000000125168f2000000014b64e87f
赏金?
我希望使用此更新样式将表单绑定到非托管实体并仅在需要时更新现有实体,而不仅仅是简单的“导入功能”。因此,我的目标是创建一个通用的适用于所有类型实体的东西。
但我当然不期望整个代码。处理托管ArrayCollection
的更新的良好实践以及在编写此功能之前应考虑的一些提示/警告应该足够了。
$form->handleRequest($fiddle);
)并且表单被正确填写($form->isValid()
返回true)时,我需要进行一些其他检查,例如查看当前用户是否能够保存它。如果他有权保存,那就没问题;但是如果他没有权限保存,我应该分离实体以避免自动更新。我在3天前发现了这个令人难以置信的自动更新,并且在完全理解发生了什么之前,再也不会使用托管实体来处理表单了。 - Alain Tiemblo@Security
注释来检查? - Jason Roman