【问题标题】:Symfony: Persisting embedded forms and avoiding duplicate entriesSymfony:持久化嵌入表单并避免重复条目
【发布时间】:2015-04-13 09:17:49
【问题描述】:

我已经在 Symfony 上玩了大约一个月了。到目前为止,我喜欢这个框架,但我遇到了一个让我对 Form 组件产生怀疑的问题。

概述 我有两种形式,每种形式用于以下实体:

  • 帖子
  • 标签

它们具有多对多的双向关系。标签表单嵌入在帖子表单中,以允许动态创建新标签并将其与帖子相关联。

问题 当使用新的标签条目时,这在启用级联的情况下工作得很好。但是,如果重新使用现有标签条目,则标签实体会触发唯一约束违规。嵌入式表单基本上作为一个实用程序仅用于创建新标签,因为我想在现有标签未插入但仅与父表单相关联的条件场景中使用它。

为了避免重复问题,我关闭了级联并与教义听众一起玩。但是,我找不到解决方法。有没有人有任何想法?我显然可以手动处理表单提交,但这会半途而废。

表单类型

  • 两种形式都扩展了“AbstractType”

控制器

  • 处理代码的特定操作看起来像这样

    $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类的摘录(Blog与前面提到的Posts相同)

    /**
     * @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;
    }
    

    `

【问题讨论】:

  • 您可以发布您的表单类型吗?如果没有一些代码,它有点难以帮助你。通常这应该工作得很好,SF2 应该只编辑关系表。
  • 好的,让我快速编辑帖子。
  • 全部完成。我只花了大约 20 分钟,因为我不知何故也设法打破了这里的表格,迫使我手动写下代码以进行适当的缩进。就是这样的一天。

标签: php forms symfony doctrine-orm


【解决方案1】:

解决方案

诀窍是订阅事件侦听器,然后手动调整持久化集合/UnitOfWork API。

使用教义.event_listener

这应该让你开始:

Prevent duplicates in the database in a many-to-many relationship

使用教义.orm.entity_listener

这样做的好处是监听器只被调用到指定的实体。

我的问题通过将孩子订阅到实体侦听器并在“preFlush”事件期间处理重复项得到解决。

这是你必须做的:

  1. 使用以下注释实体类(在本例中为标记):

@ORM\EntityListeners({"\PathToListener\TagListener"})
  1. 这是进入事件侦听器的一段代码(它需要重构,但它应该可以帮助您开始)

/** @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 的创建者错过了嵌入表单的技巧(不太可能),要么我还有很多关于对象模型的知识。

【讨论】:

  • 嗨@captainspi,我有一个同样的问题我无法解决,你能看看here并帮助我吗?因为我读了你的答案,但我不知道该怎么做,我是新手,谢谢。
猜你喜欢
  • 2019-12-08
  • 2012-08-24
  • 1970-01-01
  • 2012-12-14
  • 2019-10-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
相关资源
最近更新 更多