title: Symfony 3.3: unable to add and remove items of the same entity with collection form type
body: <p>I am new to Symfony, so I started with the official tutorial, installed Symfony framework 3.3.2 and worked through the tutorial while customizing entities, controllers, forms and views to my specific needs.</p>
  
  <p>So basically I have an entity named BasePreset, there are already a couple of rows in the DB, and I finally have managed to create a form collection type that renders a list of editable BasePreset entity fields with 'add' and 'remove' links: the 1st adds new blank fields to the list, and every 'remove' link removes the corresponding fields from DOM. Everything according to docs.</p>
  
  <p>So I succeed to update the existing fields (I see the right changes in form HTML after reloading and in the DB as well).</p>
  
  <p>The problem is, add/delete not working. No errors given. Checked in Chrome Dev tools: parameters sent as expected.</p>
  
  <p>I used the following documentation (and a lot of googling of course) regarding the form builder:</p>
  
  <p><a href="http://symfony.com/doc/current/forms.html" rel="nofollow noreferrer">http://symfony.com/doc/current/forms.html</a></p>
  
  <p><a href="https://symfony.com/doc/current/best_practices/forms.html" rel="nofollow noreferrer">https://symfony.com/doc/current/best_practices/forms.html</a></p>
  
  <p><a href="https://symfony.com/doc/current/form/form_collections.html" rel="nofollow noreferrer">https://symfony.com/doc/current/form/form_collections.html</a></p>
  
  <p><a href="https://symfony.com/doc/current/reference/forms/types/collection.html" rel="nofollow noreferrer">https://symfony.com/doc/current/reference/forms/types/collection.html</a></p>
  
  <p>Now, in <a href="https://symfony.com/doc/current/form/form_collections.html" rel="nofollow noreferrer">this doc</a> is stated:</p>
  
  <blockquote>
    <p>You have to create both addTag() and removeTag() methods, otherwise
    the form will still use setTag() even if by_reference is false. You'll
    learn more about the removeTag() method later in this article.</p>
  </blockquote>
  
  <p>At this moment I don't have any referenced subentity as it is described in the examples. I just want be able to edit the same plain entity, including adding new items and deleting existing. Maybe I'm wrong, but this seems as kind of a trivial basic goal in my mind. I don't understand how to properly add 'setBasePreset' and 'removeBasePreset' methods to the 'BasePreset' entity itself.</p>
  
  <p>Of course I can skip using the form builder, but I'd like to leverage its power as a part of the framework. Any advice, example, maybe pointing to some relevant doc/tutorial that I missed - will be greatly appreciated.</p>
  
  <p><strong>Post example (with one  addition and one removal):</strong></p>
  
  <pre><code>base_presets[base_presets][5][name]:new_preset
  base_presets[base_presets][5][description]:new, not really added
  base_presets[base_presets][0][name]:ffffas44432df
  base_presets[base_presets][0][description]:asdfffff2333
  base_presets[base_presets][2][name]:ffffasdf2222
  base_presets[base_presets][2][description]:asdff3fff2333
  base_presets[base_presets][3][name]:yoyoshka
  base_presets[base_presets][3][description]:nananaf
  base_presets[base_presets][4][name]:123fffdsaasdf
  base_presets[base_presets][4][description]:pop123
  base_presets[_token]:H2QwRHdvZW1WAdc6VTONnspxvH1U-oC8rCEEprDdMCQ
  </code></pre>
  
  <p><strong>The 'BasePresetsType' class:</strong></p>
  
  <pre><code>&lt;?php
  namespace AppBundle\Form;
  
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolver;
  use Symfony\Component\Form\Extension\Core\Type\CollectionType;
  
  class BasePresetsType extends AbstractType {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('base_presets', CollectionType::class, array(
              'entry_type' =&gt; BasePresetType::class,
              'allow_add' =&gt; true,
              'allow_delete' =&gt; true,
              'by_reference' =&gt; false // also tried 'true' but without any success
          ));
      }
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; null,
          ));
      }
  }
  </code></pre>
  
  <p><strong>The 'BasePresetType' class:</strong></p>
  
  <pre><code>&lt;?php
  namespace AppBundle\Form;
  
  use AppBundle\Entity\BasePreset;
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolver;
  use Symfony\Component\Form\Extension\Core\Type\TextType;
  use Symfony\Component\Form\Extension\Core\Type\TextareaType;
  
  class BasePresetType extends AbstractType {
      public function buildForm(FormBuilderInterface $builder, array $options) {
          $builder
              -&gt;add('name', TextType::class)
              -&gt;add('description', TextareaType::class);
      }
  
      public function configureOptions(OptionsResolver $resolver) {
          $resolver-&gt;setDefaults([
              'data_class' =&gt; BasePreset::class,
          ]);
      }
  }
  </code></pre>
  
  <p><strong>The controller method:</strong></p>
  
  <pre><code>&lt;?php
  // ...
      /**
       * @Route("/admin/list_base_presets", name="list_base_presets")
       */
      public function listBasePresetsAction(Request $request, EntityManagerInterface $em, LoggerInterface $logger) {
          $base_presets = $em-&gt;getRepository("AppBundle\Entity\BasePreset")-&gt;findAll();
  
          $form = $this-&gt;createForm(BasePresetsType::class, array('base_presets' =&gt; $base_presets));
  
          $form-&gt;handleRequest($request);
          $current_route = $request-&gt;get('_route');
  
          if ($form-&gt;isSubmitted() &amp;&amp; $form-&gt;isValid()) {
              foreach ($base_presets as $base_preset) {
                  $em-&gt;persist($base_preset);
              }
              $em-&gt;flush();
              // ... do other work - like sending an email, etc..
              // maybe set a 'flash' success message for the user
              return $this-&gt;redirectToRoute($current_route);
          }
          return $this-&gt;render('manage_params/list_base_presets.html.twig', array(
              'form' =&gt; $form-&gt;createView()
          ));
      }
  </code></pre>
  
  <p><strong>The view:</strong></p>
  
  <pre><code>{% extends "base.html.twig" %}
  
  {% block body %}
      {{ form_start(form) }}
      {{ form_label(form.base_presets) }}
      {{ form_errors(form.base_presets) }}
      &lt;ul class="base-presets" data-prototype="{{ form_widget(form.base_presets.vars.prototype)|e('html_attr') }}"&gt;
          {% for base_preset in form.base_presets %}
              &lt;li class="base-preset-item"&gt;
                  {{ form_errors(base_preset) }}
                  {{ form_widget(base_preset) }}
              &lt;/li&gt;
          {% endfor %}
          &lt;li class="form-submit"&gt;
              &lt;input type="submit" value="Submit" class="btn btn-default pull-right" /&gt;
          &lt;/li&gt;
      &lt;/ul&gt;
      {{ form_end(form) }}
  {% endblock %}
  {% block javascripts %}
      &lt;script&gt;
          var $collection_holder;
          // Setup an "add a base preset" link
          var $add_base_preset_link = $('&lt;a href="#" class="add_base_preset_link"&gt;Add a base preset&lt;/a&gt;');
          var $new_link_li = $('&lt;li&gt;&lt;/li&gt;').append($add_base_preset_link);
          $(document).ready(function () {
              // Get the ul that holds the collection of base presets
              $collection_holder = $('ul.base-presets');
              // add the "add a base preset" anchor and li to the tags ul
              $collection_holder.prepend($new_link_li);
              // count the current form inputs we have, use that as the new index when inserting a new item
              $collection_holder.data('index', $collection_holder.find('li.base-preset-item').length);
  
              $add_base_preset_link.on('click', function (e) {
                  e.preventDefault();
                  addBasePresetForm($collection_holder, $new_link_li);
              });
  
              // add a delete link to all of the existing base presets form li elements
              $collection_holder.find('li.base-preset-item').each(function () {
                  addBasePresetFormDeleteLink($(this));
              });
          });
  
          function addBasePresetForm($collection_holder, $new_link_li) {
              // Get the data-prototype
              var prototype = $collection_holder.data('prototype');
              // Get the new index
              var index = $collection_holder.data('index');
              // Replace '__name__' in the prototype's HTML to instead be a number based on how many items we have
              var new_form = prototype.replace(/__name__/g, index);
              // increment the index for the next item
              $collection_holder.data('index', index + 1);
              // Display the form in the page in an li, before the "Add a base preset" link li
              var $new_form_li = $('&lt;li class="base-preset-item"&gt;&lt;/li&gt;').append(new_form);
              $new_link_li.after($new_form_li);
              addBasePresetFormDeleteLink($new_form_li);
          }
  
          function addBasePresetFormDeleteLink($base_preset_form_li) {
              var $remove_form_a = $('&lt;a href="#"&gt;Delete this base preset&lt;/a&gt;');
              $base_preset_form_li.append($remove_form_a);
              $remove_form_a.on('click', function (e) {
                  e.preventDefault();
                  $base_preset_form_li.remove();
              })
          }
      &lt;/script&gt;
  {% endblock %}
  </code></pre>
  
  <p><strong>And, finally, the longest listing - the 'BasePreset' entity class:</strong></p>
  
  <pre><code>&lt;?php
  namespace AppBundle\Entity;
  
  use Doctrine\Common\Collections\ArrayCollection;
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Component\Validator\Constraints as Assert;
  use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  
  
  /**
   * Class BasePreset
   * @package AppBundle\Entity
   *
   * @ORM\Entity
   * @ORM\Table(name="base_presets")
   * @UniqueEntity(fields="name", message="There should be only one (unique) base preset")
   */
  class BasePreset {
      /**
       * @ORM\OneToMany(targetEntity="BaseParamsGroup", mappedBy="base_preset")
       */
      private $base_params_groups;
  
      /**
       * @var int
       *
       * @ORM\Column(type="integer")
       * @ORM\Id
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      private $id;
  
      /**
       * @var string
       *
       * @ORM\Column(type="string", length=100)
       */
      private $name;
  
      /**
       * @var string
       *
       * @Assert\NotBlank()
       * @ORM\Column(type="string", length=4000)
       */
      private $description;
  
      /**
       * @var \DateTime
       *
       * @ORM\Column(type="datetime")
       */
      private $created;
  
      /**
       * @var \DateTime
       *
       * @ORM\Column(type="datetime", columnDefinition="TIMESTAMP on update CURRENT_TIMESTAMP")
       */
      private $updated;
  
      public function __construct() {
          $this-&gt;base_params_groups = new ArrayCollection();
          $this-&gt;created = new \DateTime();
          $this-&gt;updated = new \DateTime();
      }
  
      /**
       * Get id
       *
       * @return integer
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  
      /**
       * Set name
       *
       * @param string $name
       *
       * @return BasePreset
       */
      public function setName($name)
      {
          $this-&gt;name = $name;
  
          return $this;
      }
  
      /**
       * Get name
       *
       * @return string
       */
      public function getName()
      {
          return $this-&gt;name;
      }
  
      /**
       * Set description
       *
       * @param string $description
       *
       * @return BasePreset
       */
      public function setDescription($description)
      {
          $this-&gt;description = $description;
  
          return $this;
      }
  
      /**
       * Get description
       *
       * @return string
       */
      public function getDescription()
      {
          return $this-&gt;description;
      }
  
      /**
       * Set created
       *
       * @param \DateTime $created
       *
       * @return BasePreset
       */
      public function setCreated($created)
      {
          $this-&gt;created = $created;
  
          return $this;
      }
  
      /**
       * Get created
       *
       * @return \DateTime
       */
      public function getCreated()
      {
          return $this-&gt;created;
      }
  
      /**
       * Set updated
       *
       * @param \DateTime $updated
       *
       * @return BasePreset
       */
      public function setUpdated($updated)
      {
          $this-&gt;updated = $updated;
  
          return $this;
      }
  
      /**
       * Get updated
       *
       * @return \DateTime
       */
      public function getUpdated()
      {
          return $this-&gt;updated;
      }
  
      /**
       * Add baseParamsGroup
       *
       * @param \AppBundle\Entity\BaseParamsGroup $baseParamsGroup
       *
       * @return BasePreset
       */
      public function addBaseParamsGroup(\AppBundle\Entity\BaseParamsGroup $baseParamsGroup)
      {
          $this-&gt;base_params_groups[] = $baseParamsGroup;
  
          return $this;
      }
  
      /**
       * Remove baseParamsGroup
       *
       * @param \AppBundle\Entity\BaseParamsGroup $baseParamsGroup
       */
      public function removeBaseParamsGroup(\AppBundle\Entity\BaseParamsGroup $baseParamsGroup)
      {
          $this-&gt;base_params_groups-&gt;removeElement($baseParamsGroup);
      }
  
      /**
       * Get baseParamsGroups
       *
       * @return \Doctrine\Common\Collections\Collection
       */
      public function getBaseParamsGroups()
      {
          return $this-&gt;base_params_groups;
      }
  }
  </code></pre>