Symfony:保留嵌入表单并避免重复输入

时间:2015-04-13 09:17:49

标签: php forms symfony doctrine-orm

我已经和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 类(博客与上述帖子相同)

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

    `

1 个答案:

答案 0 :(得分:1)

<强> Solutiton

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

使用doctrine.event_listener

这应该让你开始:

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

使用doctrine.orm.entity_listener

这样做的好处是只能将侦听器引发到指定的实体。

通过将子节点订阅实体侦听器并在'preFlush'事件期间处理重复项来解决我的问题。

以下是您必须做的事情:

  1. 使用以下内容注释实体类(在本例中为Tag):
  2. @ORM\EntityListeners({"\PathToListener\TagListener"})

    1. 以下是进入事件监听器的一些代码(它需要重新分解,但它应该让你开始)
    2. /** @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的创建者错过了嵌入表单的技巧(不太可能),或者我仍然需要了解对象模型。