.. index:: single: Form; Embed collection of forms How to Embed a Collection of Forms ================================== In this article, you'll learn how to create a form that embeds a collection of many other forms. This could be useful, for example, if you had a ``Task`` class and you wanted to edit/create/remove many ``Tag`` objects related to that Task, right inside the same form. Let's start by creating a ``Task`` entity:: // src/Entity/Task.php namespace App\Entity; use Doctrine\Common\Collections\ArrayCollection; class Task { protected $description; protected $tags; public function __construct() { $this->tags = new ArrayCollection(); } public function getDescription() { return $this->description; } public function setDescription($description) { $this->description = $description; } public function getTags() { return $this->tags; } } .. note:: The ``ArrayCollection`` is specific to Doctrine and is basically the same as using an ``array`` (but it must be an ``ArrayCollection`` if you're using Doctrine). Now, create a ``Tag`` class. As you saw above, a ``Task`` can have many ``Tag`` objects:: // src/Entity/Tag.php namespace App\Entity; class Tag { private $name; public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } Then, create a form class so that a ``Tag`` object can be modified by the user:: // src/Form/TagType.php namespace App\Form; use App\Entity\Tag; 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->add('name'); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Tag::class, ]); } } Next, let's create a form for the ``Task`` entity, using a :doc:`CollectionType ` field of ``TagType`` forms. This will allow us to modify all the ``Tag`` elements of a ``Task`` right inside the task form itself:: // src/Form/TaskType.php namespace App\Form; use App\Entity\Task; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('description'); $builder->add('tags', CollectionType::class, [ 'entry_type' => TagType::class, 'entry_options' => ['label' => false], ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => Task::class, ]); } } In your controller, you'll create a new form from the ``TaskType``:: // src/Controller/TaskController.php namespace App\Controller; use App\Entity\Tag; use App\Entity\Task; use App\Form\TaskType; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; class TaskController extends AbstractController { public function new(Request $request) { $task = new Task(); // dummy code - add some example tags to the task // (otherwise, the template will render an empty list of tags) $tag1 = new Tag(); $tag1->setName('tag1'); $task->getTags()->add($tag1); $tag2 = new Tag(); $tag2->setName('tag2'); $task->getTags()->add($tag2); // end dummy code $form = $this->createForm(TaskType::class, $task); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // ... do your form processing, like saving the Task and Tag entities } return $this->render('task/new.html.twig', [ 'form' => $form->createView(), ]); } } In the template, you can now iterate over the existing ``TagType`` forms to render them: .. code-block:: html+twig {# templates/task/new.html.twig #} {# ... #} {{ form_start(form) }} {{ form_row(form.description) }}