我已经和Symfony玩了大约一个月了。到目前为止,我喜欢这个框架,但是我遇到了一个让我怀疑Form组件的问题。
概述 我有两种形式,分别用于以下实体:
他们有多对多的双向关系。 “标签”表单嵌入在“帖子”表单中,以允许创建新标签并与动态关联。
问题 当使用新的标记条目时,这可以很好地启用级联。但是,如果重用现有标记条目,则标记实体将触发唯一约束违规。嵌入式表单基本上只用作创建新标签的实用程序,我想在条件场景中使用它,其中现有标签未插入但只与父表单相关联。
为了尝试避免重复问题,我关闭了级联并与教条听众一起玩。但是,我无法找到解决办法。有没有人有任何想法?我显然可以手动处理表单提交,但这会使使用表单组件的目的失败一半。
表单类型
控制器
处理代码的特定操作看起来像这样
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('B4PGround0Bundle:Blog\\Blog')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Blog entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
if ($editForm->isValid()) {
$em->flush();
实体
摘自 Blog 类(博客与上述帖子相同)
/**
* @ORM\ManyToMany(targetEntity="Tag", inversedBy="blogs")
* @ORM\JoinTable(name="tags_blogs")
* @Assert\Valid()
**/
private $tags;
.......
public function addTag($tag)
{
$tags->addBlog($this);
$this->tags[] = $tags;
return $this
}
摘自标签类
/**
* @ORM\ManyToMany(targetEntity="Blog", mappedBy="tags")
**/
private $blogs;
....
public function addBlog($blog)
{
$this->blogs[] = $blogs;
return $this;
}
`
答案 0 :(得分:1)
<强> Solutiton 强>
诀窍是订阅事件监听器,然后手动调整持久集合/ UnitOfWork API。
使用doctrine.event_listener
这应该让你开始:
Prevent duplicates in the database in a many-to-many relationship
使用doctrine.orm.entity_listener
这样做的好处是只能将侦听器引发到指定的实体。
通过将子节点订阅实体侦听器并在'preFlush'事件期间处理重复项来解决我的问题。
以下是您必须做的事情:
@ORM\EntityListeners({"\PathToListener\TagListener"})
/** @ORM\preFlush */
public function prePersist(Tag $entities, PreFlushEventArgs $args)
{
//retrieve the entity manager
$em = $args->getEntityManager();
//retrieve the unitOfwork API
$uow = $em->getUnitOfWork();
//retrieve the child entities repo
$tagRepo = $em->getRepository("\Path2ChildEntity\Tag");
//retrieve all the entities scheduled for update
foreach ($uow->getScheduledEntityUpdates() as $blog) {
//we are only interested in handling blog posts
if(!($blog instanceof \Path2ParentEntity\Blog))
{
continue;
}
//retrieve all the tags associated with the blog post entity
$tagList = $blog->getTags();
//lets cycle through each retrieved tag, one at a time
foreach($tagList as $key=>$tagItem){
//lets see if we can find a tag by this name in the repo
$tmpTag = $tagRepo->findOneBy(array("name"=>$tagItem->getName()));
//if the tag already exists, we don't want to insert it.
if($tmpTag !== null)
{
//lets cycle through all the entities scheduledd for insertion
foreach($uow->getScheduledEntityInsertions() as $items) {
//if the types match and the names checkout too, remove
the item from the insertion list
if(($items instanceof \Path2ChildEntity\Tag) && ($items->getName()===$tmpTag->getName()))
{
$uow->remove($items);
}
}
//adjust the blog post entity by replacing the original tag with the one from the database
$blog->removeTag($tagList[$key]);
$tagList[$key] = $tmpTag;
}
}
$metadata = $em->getClassMetadata('\Path2ChildEntity\Tag');
//lets ask UOW to recompute the changes that have been made to the Tag and Blog entities since the preFlush event was fired
foreach ($tagList as $tag) {
$uow->recomputeSingleEntityChangeSet($metadata, $tag);
}
$metadata = $em->getClassMetadata('\Path2ParentEntity\Blog');
$uow->recomputeSingleEntityChangeSet($metadata, $blog);
}
}
对于凌乱的代码人员感到抱歉,但我希望它可以帮助某人。
PS因为我是Doctrine的新手,并且仍然在围绕对象进行建模而不是表格,我有一种沉闷的感觉,我的设计应该归咎于我遇到的问题。我的意思是,得到像这样简单的东西应该不是那么复杂。 Symfony的创建者错过了嵌入表单的技巧(不太可能),或者我仍然需要了解对象模型。