如果更新条目,新的collection元素将覆盖最后一个元素(如果已经存在) [英] New collection element overwrites the last element if it already exists when updating an entry

查看:64
本文介绍了如果更新条目,新的collection元素将覆盖最后一个元素(如果已经存在)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我有一个Mission实体通过ManyToMany关系连接到Option实体.在创建新任务时,可以添加几个选项(这是可以的).现在,当更新任务时,当我添加新选项时,它将覆盖最后一个选项(如果已经存在),否则将非常适合.我希望能够在修改任务时添加尽可能多的选项,而不会覆盖现有的最后一个选项. 我发布了我的代码:

so I have a Mission entity connected to an Option entity through a ManyToMany relationship. At the time of creating a new mission it is possible to add several options (this point is ok). Now when updating a mission, when I add a new option it overwrites the last option if it already exists otherwise it fits well. I want to be able to add as many options when modifying a mission without overwriting the last existing option. I post my code:

任务实体:

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

OptionType:

OptionType:

        $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
            ])

在MissionController中:

In MissionController:

    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>

放映: 显示

添加新内容时:

更新后:

在此先感谢您的帮助

推荐答案

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

The observations you describe are consistent with the following ...

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

When your form is created, it adds fields with the names

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]

,当您添加其他两个选项时,它会添加:

and when you add two other option, it adds:

mission_edit[options][0][tagname]
mission_edit[options][0][prix]
mission_edit[options][1][tagname]
mission_edit[options][1][prix]

然后您将第一个选项(包括原始选项)留给了您.

and then you scrap the first option leaving you with (including the original 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]

,由于 php 将始终覆盖重复的var名称...您将使用新选项覆盖原始的第二个选项(索引为1).

and since php will always overwrite duplicate var names ... you overwrite your original second option (with the index 1) with the new option.

您应该通过实际查看DOM进行验证.如果我的假设是正确的,那么您会聪明地"在树枝模板中省略表单的特定部分,以创建原始元素,该元素可能位于 outside <ul>之外,而导致<ul上的c2>计数器为0,并相应地覆盖元素.

You should verify by actually looking at the DOM. If my assumption is correct, than you "cleverly" omitted the specific part of your form in your twig templates, where the original elements are created, which is probably outside the <ul> which leads to the index counter on the ul to be 0, and overwriting the elements accordingly.

老实说,出于这个原因,我不喜欢使用symfony示例中的特定代码行-您也正在使用-

To be honest, I dislike using a specific line of code from the symfony example - which you're also using - for exactly that reason, the line of code is:

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

因为它使用了一组非显而易见的假设,特别是:您的子表单至少具有一个输入字段类型,事实并非如此! (例如,可编辑+大量的js),并且与您的示例相关,所有现有的子级都放在集合所有者中,而您不会这样做.为了公平起见,在这里显示出来的symfony示例是绝对可行的,因为这里的假设成立.

since it uses a non-obvious set of assumptions, particularly: your sub-form has at least one input type of field, which might not be the case! (e.g. editable + lots of js), and relevant for your example, that all existing children are put inside the collection holder, which you don't do. To be fair the symfony example where this is shown absolutely works, since the assumptions hold true there.

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

Instead* I would use (* meaning, removing the other line of code!)

<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>

因为它正确地将索引设置为应该的子代数.

because it correctly sets the index to the number of children, which it is supposed to be.

这篇关于如果更新条目,新的collection元素将覆盖最后一个元素(如果已经存在)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆