title: Persisting a collection of embedded forms using @ORM\OneToMany(cascade={"persist"})
body: <p>I am trying to embed a collection of forms inside a form. This topic is new to me. I have created a simple task form with a description field, from inside the task form you can add and remove 'n' number of tag forms by clicking on 'Add a tag' link. The tag field has only one field 'name'. Using doctrine I've established a One to Many bidirectional relationship between, the Task and the Tag entities, with Tag being the owning side. Upon submission all the data, except for the task_id(referenced) column in the Tag table is getting successfully saved in the database.
  <a href="https://i.stack.imgur.com/fURaf.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/fURaf.png" alt="enter image description here"></a></p>
  
  <p>As I am using <code>cascade={"persist"}</code> in my OneToMany metadata, so the doctrine does the persist operation automatically from the Task object to any related tags. However in this case my guess is that the Doctrine is persisting the Tag form first and the Task form after it, thus the user_id  value of the Task is not appearing in the Tag table for the related Tag. I know that the workaround for <code>cascade={"persist"}</code> approach is to persist the added Tag forms manually, but that would increase the size and complexity of my code. My question is, How do I make the <code>cascade={"persist"}</code> approach work? Also another bonus question, when I try to add the new tag objects in the Task class by calling the custom addTag() method by setting the <code>'by_reference' =&gt; false</code> inside the options array of 'tags' CollectionType field in the TaskType.php, after the form submission I get a blank page only stating Server Error, without the details of error. 
  Below is the Task entity:</p>
  
  <pre><code>&lt;?php
  
  namespace AppBundle\Entity;
  
  use Doctrine\Common\Collections\ArrayCollection;
  use Doctrine\ORM\Mapping as ORM;
  
  /**
   * Task
   *
   * @ORM\Table(name="Task")
   * @ORM\Entity(repositoryClass="AppBundle\Repository\TaskRepository")
   */
  class Task
  {
      /**
       * @var int
       *
       * @ORM\Column(name="id", type="integer")
       * @ORM\Id
       * @ORM\GeneratedValue(strategy="AUTO")
       */
      protected $id;
  
      /**
       * 
       * @var string
       *
       * @ORM\Column(name="Description", type="string")
       */
      protected $description;
  
      /**
       * @ORM\OneToMany(targetEntity="Tag", mappedBy="tasks",cascade={"persist"})
       */
      protected $tags;
  
      /**
       * @return ArrayCollection 
       */
      public function __construct()
      {
          $this-&gt;tags = new ArrayCollection();
      }
  
  
      /**
       * Get description
       *
       * @return string 
       */
      public function getDescription()
      {
          return $this-&gt;description;
      }
  
      /**
       * Set description
       *
       * @param string $description
       * @return Task
       */
      public function setDescription($description)
      {
          $this-&gt;description = $description;
  
          return $this;
      }
  
      /**
       * Get tags
       *
       * @return Tags 
       */
      public function getTags()
      {
          return $this-&gt;tags;
      }
  
      /**
       * Set tags
       *
       * @param ArrayCollection $tags
       * @return Task
       */
      public function setTags($tags)
      {
          $this-&gt;tags = $tags;
  
          return $this;
      }
  
      /**
       * Get id
       *
       * @return integer 
       */
      public function getId()
      {
          return $this-&gt;id;
      }
  
  //    /**
  //     * Add tag
  //     * 
  //     * @param Tag
  //     * @return Task 
  //     */
  //    public function addTag(Tag $tag)
  //{
  //    $tag-&gt;addTask($this);
  //
  //    $this-&gt;tags-&gt;add($tag);
  //    
  //    return $this;
  //}
  
  //    /**
  //     * Remove tags
  //     *
  //     * @param \AppBundle\Entity\Tag $tags
  //     */
  //    public function removeTag(\AppBundle\Entity\Tag $tags)
  //    {
  //        $this-&gt;tags-&gt;removeElement($tags);
  //    }
  
     }
  </code></pre>
  
  <p>Below is the Tag entity:</p>
  
  <pre><code>&lt;?php
  
  namespace AppBundle\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  
  /**
   * Tag
   *
   * @ORM\Table(name="Tag")
   * @ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
   */
  class Tag {
  
      /**
       * @var int
       *
       * 
       * @ORM\Id
       * @ORM\GeneratedValue(strategy="AUTO")
       * @ORM\Column(name="id", type="integer")
       */
      public $id;
  
      /**
       * 
       * @var string
       *
       * @ORM\Column(name="Name", type="string")
       */
      public $name;
  
      /**
       * @ORM\ManyToOne(targetEntity="Task", inversedBy="tags", cascade={"persist"})
       * @ORM\JoinColumn(name="task_id", referencedColumnName="id")
       */
      protected $tasks;
  
      /**
       * Set name
       *
       * @param string $name
       * @return Tag
       */
      public function setName($name) {
          $this-&gt;name = $name;
  
          return $this;
      }
  
      /**
       * Get name
       *
       * @return string 
       */
      public function getName() {
          return $this-&gt;name;
      }
  
      /**
       * Get id
       *
       * @return integer 
       */
      public function getId() {
          return $this-&gt;id;
      }
  
      /**
       * Set tasks
       *
       * @param \AppBundle\Entity\Task $tasks
       * @return Tag
       */
      public function setTasks(\AppBundle\Entity\Task $tasks = null) {
          $this-&gt;tasks = $tasks;
  
          return $this;
      }
  
      /**
       * Get tasks
       *
       * @return \AppBundle\Entity\Task 
       */
      public function getTasks() {
          return $this-&gt;tasks;
      }
  
  //    /**
  //     * Add Task
  //     * 
  //     * @param Task
  //     * @return Tag 
  //     */
  //    public function addTask(Task $task) {
  //        if (!$this-&gt;tasks-&gt;contains($task)) {
  //            $this-&gt;tasks-&gt;add($task);
  //        }
  //      }
  
     }
  </code></pre>
  
  <p>Below is the Tag form class:</p>
  
  <pre><code>&lt;?php
  
  namespace AppBundle\Form;
  
  use Symfony\Component\Form\AbstractType;
  use Symfony\Component\Form\FormBuilderInterface;
  use Symfony\Component\OptionsResolver\OptionsResolver;
  
  class TagType extends AbstractType
  {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {
          $builder-&gt;add('name');
      }
  
      public function configureOptions(OptionsResolver $resolver)
      {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'AppBundle\Entity\Tag',
          ));
      }
  }
  </code></pre>
  
  <p>Below is the Task form class:</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;
  use Symfony\Component\Form\Extension\Core\Type\SubmitType;
  
  class TaskType extends AbstractType {
  
  
      public function buildForm(FormBuilderInterface $builder, array $options) {
          $builder-&gt;add('description');
  
          $builder-&gt;add('tags', CollectionType::class, array(
              'entry_type' =&gt; TagType::class,
              'allow_add' =&gt; true,
              'allow_delete' =&gt; true,
  //            'by_reference' =&gt; false,
          ))
          -&gt;add('save', SubmitType::class, array('label' =&gt; 'Submit'));
      }
  
      public function configureOptions(OptionsResolver $resolver) {
          $resolver-&gt;setDefaults(array(
              'data_class' =&gt; 'AppBundle\Entity\Task',
          ));
      }
  
  }
  </code></pre>
  
  <p>Below is the AddTagg.js:</p>
  
  <pre><code>// setup an "add a tag" link
  var $addTagLink = $('&lt;a href="#" class="add_tag_link"&gt;Add a tag&lt;/a&gt;');
  var $newLinkLi = $('&lt;li&gt;&lt;/li&gt;').append($addTagLink);
  
  jQuery(document).ready(function() {
      // Get the ul that holds the collection of tags
     var $collectionHolder = $('ul.tags');
  
      // add a delete link to all of the existing tag form li elements
      $collectionHolder.find('li').each(function() {
          addTagFormDeleteLink($(this));
      });
  
      // add the "add a tag" anchor and li to the tags ul
      $collectionHolder.append($newLinkLi);
  
      // count the current form inputs we have (e.g. 2), use that as the new
      // index when inserting a new item (e.g. 2)
      $collectionHolder.data('index', $collectionHolder.find(':input').length);
  
      $addTagLink.on('click', function(e) {
          // prevent the link from creating a "#" on the URL
          e.preventDefault();
  
          // add a new tag form (see code block below)
          addTagForm($collectionHolder, $newLinkLi);
      });
  
  
  });
  
  function addTagForm($collectionHolder, $newLinkLi) {
      // Get the data-prototype explained earlier
      var prototype = $collectionHolder.data('prototype');
  
      // get the new index
      var index = $collectionHolder.data('index');
  
      // Replace '$$name$$' in the prototype's HTML to
      // instead be a number based on how many items we have
      var newForm = prototype.replace(/__name__/g, index);
  
      // increase the index with one for the next item
      $collectionHolder.data('index', index + 1);
  
      // Display the form in the page in an li, before the "Add a tag" link li
      var $newFormLi = $('&lt;li&gt;&lt;/li&gt;').append(newForm);
  
      // also add a remove button, just for this example
      $newFormLi.append('&lt;a href="#" class="remove-tag"&gt;x&lt;/a&gt;');
  
      $newLinkLi.before($newFormLi);
  
      // handle the removal, just for this example
      $('.remove-tag').click(function(e) {
          e.preventDefault();
  
          $(this).parent().remove();
  
          return false;
      });
  
      // add a delete link to the new form
      addTagFormDeleteLink($newFormLi);
  }
  
  function addTagFormDeleteLink($tagFormLi) {
      var $removeFormA = $('&lt;a href="#"&gt;delete this tag&lt;/a&gt;');
      $tagFormLi.append($removeFormA);
  
      $removeFormA.on('click', function(e) {
          // prevent the link from creating a "#" on the URL
          e.preventDefault();
  
          // remove the li for the tag form
          $tagFormLi.remove();
      });
  }
  </code></pre>
  
  <p>Below is the twig file:</p>
  
  <pre><code>{% extends 'base.html.twig' %}
  
  {% block body %}
  
      &lt;h3&gt;Embedded Collection of Forms!&lt;/h3&gt;
  
      {% javascripts '@AppBundle/Resources/public/js/jquery-2.2.3.min.js' %}
      &lt;script src="{{ asset_url }}"&gt;&lt;/script&gt;
      {% endjavascripts %}
  
      {% javascripts '@AppBundle/Resources/public/js/AddTagg.js' %}
      &lt;script src="{{ asset_url }}"&gt;&lt;/script&gt;
      {% endjavascripts %}
  
      {{ form_start(form) }}
      {# render the task's only field: description #}
      {{ form_row(form.description) }}
  
      &lt;h3&gt;Tags&lt;/h3&gt;
      &lt;ul class="tags" data-prototype="{{ form_widget(form.tags.vars.prototype)|e }}"&gt;
          {# iterate over each existing tag and render its only field: name #}
          {% for tag in form.tags %}
              &lt;li&gt;{{ form_row(tag.name) }}&lt;/li&gt;
              {% endfor %}
      &lt;/ul&gt;
      {{ form_end(form) }}
  {% endblock %}
  </code></pre>
  
  <p>Below is the controller:</p>
  
  <pre><code>&lt;?php
  
  namespace AppBundle\Controller;
  
  use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
  use Symfony\Bundle\FrameworkBundle\Controller\Controller;
  use Symfony\Component\HttpFoundation\Request;
  use AppBundle\Entity\Task;
  use AppBundle\Entity\Tag;
  use AppBundle\Form\TaskType;
  use Doctrine\Common\Collections\ArrayCollection;
  
  class DefaultController extends Controller {
  
      /**
       * @Route("/", name="homepage")
       */
      public function indexAction(Request $request) {
          $task = new Task();
  
          $form = $this-&gt;createForm(TaskType::class, $task);
  
          $form-&gt;handleRequest($request);
  
          if ($form-&gt;isValid()) {
  
  
              $em = $this-&gt;getDoctrine()-&gt;getManager();
              $em-&gt;persist($task);
              $em-&gt;flush();
          }
  
          return $this-&gt;render('default/index.html.twig', array(
                      'form' =&gt; $form-&gt;createView(),
          ));
      }
  
      /**
       * @Route("/edit", name="editpage")
       */
      public function editAction($id, Request $request) {
          $em = $this-&gt;getDoctrine()-&gt;getManager();
          $task = $em-&gt;getRepository('AppBundle:Task')-&gt;find($id);
  
          if (!$task) {
              throw $this-&gt;createNotFoundException('No task found for id ' . $id);
          }
  
          $originalTags = new ArrayCollection();
  
          // Create an ArrayCollection of the current Tag objects in the database
          foreach ($task-&gt;getTags() as $tag) {
              $originalTags-&gt;add($tag);
          }
  
          $editForm = $this-&gt;createForm(TaskType::class, $task);
  
          $editForm-&gt;handleRequest($request);
  
          if ($editForm-&gt;isValid()) {
  
              // remove the relationship between the tag and the Task
              foreach ($originalTags as $tag) {
                  if (false === $task-&gt;getTags()-&gt;contains($tag)) {
                      // remove the Task from the Tag
                      $tag-&gt;getTasks()-&gt;removeElement($task);
  
                      // if it was a many-to-one relationship, remove the relationship like this
                      $tag-&gt;setTask(null);
  
                      $em-&gt;persist($tag);
  
                      // if you wanted to delete the Tag entirely, you can also do that
                      // $em-&gt;remove($tag);
                  }
              }
  
              $em-&gt;persist($task);
              $em-&gt;flush();
  
              // redirect back to some edit page
              return $this-&gt;redirectToRoute('task_edit', array('id' =&gt; $id));
          }
  
          // render some form template
          return $this-&gt;render('default/index.html.twig', array(
                      'form' =&gt; $editForm-&gt;createView(),
          ));
      }
  
  }
  </code></pre>