Collections 更新条目时,如果最后一个元素已存在,则新集合元素将覆盖该元素

Collections 更新条目时,如果最后一个元素已存在,则新集合元素将覆盖该元素,collections,many-to-many,symfony4,Collections,Many To Many,Symfony4,所以我有一个任务实体通过多个关系连接到一个选项实体。在创建新任务时,可以添加几个选项(这一点没有问题)。现在,当更新任务时,当我添加一个新选项时,它会覆盖上一个选项(如果它已经存在),否则它很适合。我希望能够在修改任务时添加尽可能多的选项,而不覆盖上一个现有选项。 我发布我的代码: 特派团实体: /** * @var ArrayCollection $options * * @ORM\ManyToMany(targetEntity="App\Entity\O

所以我有一个任务实体通过多个关系连接到一个选项实体。在创建新任务时,可以添加几个选项(这一点没有问题)。现在,当更新任务时,当我添加一个新选项时,它会覆盖上一个选项(如果它已经存在),否则它很适合。我希望能够在修改任务时添加尽可能多的选项,而不覆盖上一个现有选项。 我发布我的代码:

特派团实体:

    /**
     * @var ArrayCollection $options
     *
     * @ORM\ManyToMany(targetEntity="App\Entity\Option", inversedBy="missionOptions", cascade={"persist","remove"})
     * @ORM\JoinColumn(nullable=true)
     */
    protected $options;

    /**
     * Mission constructor.
     */
    public function __construct()
    {
        $this->options = new ArrayCollection();
    }

    public function addOption(Option $option)
    {
        $tag->addMission($this);
        $this->options->add($option);
        return $this;
    }


    public function addOptions($options)
    {
        foreach($options as $option){
            $this->addOption($option);
        }
        return $this;
    }

    public function removeOption(Option $option)
    {
        $this->options->removeElement($option);
        return $this;
    }

    public function removeOptions($options)
    {
        foreach($options as $option){
            $this->removeOption($option);
        }
        return $this;
    }

    public function setOptions(Option $option)
    {
        $this->options[] = $option;
        return $this;
    }

    /**
     * Get options
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getOptions()
    {
        return $this->options;
    } 
期权实体:

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Mission", mappedBy="options", cascade={"persist","remove"})
     */
    private $missionOptions;

    /**
     * Option constructor.
     */
    public function __construct()
    {
        $this->missionOptions = new ArrayCollection();
    }

    public function setMission($missions)
    {
        $this->missionOptions = $missions;
    }

    public function getMission()
    {
        return $this->missionOptions;
    }

    public function addMission(Mission $mission)
    {
        if (!$this->missionOptions->contains($mission)) {
            $this->missionOptions->add($mission);
        }
        return $this;
    }
选项类型:

        $builder->add('tagname', TextType::class, array(
                'required' => true,
                'translation_domain' => 'FOSUserBundle',
                'label' => 'form.option_name',
                'attr' => array(
                    'class' => 'with-border',
                    'placeholder' => 'Nouvelle option'
                )
            ))
            ->add('prix', NumberType::class, array(
                'required' => true,
                'translation_domain' => 'FOSUserBundle',
                'label' => 'form.option_price',
                'attr' => array(
                    'min' => '2000',
                    'class' => 'with-border',
                    'placeholder'  => 'prix'
                )
            ))
        ;
任务类型:

          $builder->add('options', CollectionType::class, [
                'entry_type' => OptionType::class,
                'entry_options' => ['label' => false],
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'required' => false
            ])
在任务控制器中:

    public function updateAction(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();
        $entity = $em->getRepository('App:Mission')->find($id);

        $originalOptions = new ArrayCollection();
        foreach ($entity->getOptions() as $option) {
            $originalOptions->add($option);
        }
        $editForm = $this->createEditForm($entity);
        $editForm->handleRequest($request);
        if ($editForm->isSubmitted() && $editForm->isValid()) {

            foreach ($originalOptions as $option) {
                if ($option->getTagname() == null || $option->getPrix() == null) {
                    $option->getMission()->removeElement($entity);
                    $em->persist($option);
                    $em->remove($option);
                }
            }

            $em->flush();
            return $this->redirectToRoute('dashboard_missions');
        }
     }
在我的树枝上:

<ul class="options" data-prototype="{{ form_widget(edit_form.options.vars.prototype)|e('html_attr') }}"></ul>

<script>
        var $collectionHolder;

        var $addTagButton = $('<button type="button" class="button add_option_link"> Ajouter une option</button>');
        var $newLinkLi = $('<li></li>').append($addTagButton);

        jQuery(document).ready(function() {
            $collectionHolder = $('ul.options');
            $collectionHolder.append($newLinkLi);
            $collectionHolder.data('index', $collectionHolder.find(':input').length);
            $addTagButton.on('click', function(e) {
                addTagForm($collectionHolder, $newLinkLi);
            });
        });

        function addTagForm($collectionHolder, $newLinkLi) {
            var prototype = $collectionHolder.data('prototype');
            var index = $collectionHolder.data('index');
            var newForm = prototype;
            newForm = newForm.replace(/__name__/g, index);
            $collectionHolder.data('index', index + 1);
            var $newFormLi = $('<li></li>').append(newForm);
            $newLinkLi.before($newFormLi);
            addTagFormDeleteLink($newFormLi);
        }

        function addTagFormDeleteLink($tagFormLi) {
            var $removeFormButton = $('<button style="margin-left: 10px" type="button" class="button"></button>');
            $tagFormLi.append($removeFormButton);
            $removeFormButton.on('click', function(e) {
                $tagFormLi.remove();
            });
        }
    </script>
    var$collectionHolder; 变量$addTagButton=$('ajourne选项'); 变量$newLinkLi=$('
  • ).append($addTagButton); jQuery(文档).ready(函数(){ $collectionHolder=$('ul.options'); $collectionHolder.append($newLinkLi); $collectionHolder.data('index',$collectionHolder.find(':input').length); $addTagButton.on('click',函数(e){ addTagForm($collectionHolder,$newLinkLi); }); }); 函数addTagForm($collectionHolder,$newLinkLi){ var prototype=$collectionHolder.data('prototype'); var索引=$collectionHolder.data('index'); var newForm=原型; newForm=newForm.replace(/\u\u name\u\u/g,索引); $collectionHolder.data('index',index+1); 变量$newFormLi=$('
  • ')。追加(newForm); $newLinkLi.before($newFormLi); addTagFormDeleteLink($newFormLi); } 函数addTagFormDeleteLink($tagFormLi){ 变量$removeFormButton=$(''); $tagFormLi.append($removeFormButton); $removeFormButton.on('click',函数(e){ $tagFormLi.remove(); }); }
    筛选: 显示编辑:

    添加新内容时:

    更新后:


    提前感谢您提供的所有帮助

    您描述的观察结果与以下一致

    创建表单时,它会添加带有名称的字段

    mission_edit[options][0][tagname]
    mission_edit[options][0][prix]
    mission_edit[options][1][tagname]
    mission_edit[options][1][prix]
    mission_edit[options][2][tagname]
    mission_edit[options][2][prix]
    
    当您添加另外两个选项时,它会添加:

    mission_edit[options][0][tagname]
    mission_edit[options][0][prix]
    mission_edit[options][1][tagname]
    mission_edit[options][1][prix]
    
    然后,您将放弃第一个选项(包括原始选项!)

    由于php总是覆盖重复的变量名。。。您可以使用新选项覆盖原来的第二个选项(使用索引
    1

    您应该通过实际查看DOM来进行验证。如果我的假设是正确的,那么您“聪明地”在细枝模板中省略了表单的特定部分,其中创建了原始元素,这可能在
    之外,从而导致
    ul
    上的
    索引计数器变为
    0
    ,并相应地覆盖元素

    老实说,我不喜欢使用symfony示例中的特定代码行(您也在使用该代码行),正是因为这个原因,代码行是:

    $collectionHolder.data('index',$collectionHolder.find(':input').length)

    因为它使用了一组不明显的假设,特别是:您的子表单至少有一个输入类型的字段,这可能不是事实!(例如,可编辑+大量js),并且与您的示例相关,所有现有子项都放在收藏夹中,而您不这样做。公平地说,symfony的例子显示了这一点,这是绝对有效的,因为这里的假设是正确的

    相反*我会使用(*表示删除另一行代码!)

      数据索引=“{edit_form.options.children | length}}>{{#我在你的小树枝模板中看不到,
      options
      子表单实际上是呈现的,只是一个基本上为空的
      。。。
      mission_edit[options][0][tagname]
      mission_edit[options][0][prix]
      mission_edit[options][1][tagname]
      mission_edit[options][1][prix]
      mission_edit[options][2][tagname]
      mission_edit[options][2][prix]
      mission_edit[options][1][tagname]
      mission_edit[options][1][prix]
      
      <ul class="options" 
          data-prototype="{{ form_widget(edit_form.options.vars.prototype)|e('html_attr') }}"
          data-index="{{ edit_form.options.children|length }}"> {# <- this is the important part!! #}
      
          {# I would also add the existing children here, but there may be reasons not to! #}
      </ul>