For completeness, we find below a complete example.
You should create a new model that represents the desired shape. The fact is that you probably do not want to influence Doctrine (for example, see the doctrine: schema: update command). He may try to create a table for an entity that does not actually exist. To avoid this, simply put your model class in the Model folder (\ src \ Acme \ Bundle \ DemoBundle \ Model \ TaskList.php).
Suppose the following class of form TaskType:
<?php namespace Acme\Bundle\DemoBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class TaskType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('id', null, array('read_only' => true)) ->add('name'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults( array( 'data_class' => 'Acme\Bundle\DemoBundle\Entity\Task' ) ); } public function getName() { return 'acme_demo_task'; } }
This should be the class of the TaskList model:
<?php namespace Acme\Bundle\DemoBundle\Model; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; class TaskList { private $tasks; public function __construct() { $this->tasks = new ArrayCollection(); } public function addTask(\Acme\Bundle\DemoBundle\Entity\Task $task) { $this->tasks[] = $task; return $this; } public function removeTask(\Acme\Bundle\DemoBundle\Entity\Task $task) { $this->tasks->remove($task); return $this; } public function getTasks() { return $this->tasks; } public function setTasks(\Doctrine\Common\Collections\Collection $tasks) { $this->tasks = $tasks; return $this; } public function setFromPagination(\Knp\Component\Pager\Pagination\PaginationInterface $pagination) { foreach ($pagination as $task) { $this->addTask($task); } return $this; } }
And find below the TaskListType class:
<?php namespace Acme\Bundle\DemoBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class TaskListType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add( 'tasks', 'collection', array( 'type' => new \Acme\Bundle\DemoBundle\Form\TaskType(), ) ) ->add('save', 'submit'); } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults( array( 'data_class' => 'Acme\Bundle\DemoBundle\Model\TaskList' ) ); } public function getName() { return 'acme_demo_task_list'; } }
And your services.yml (optional):
services: acme.demo.form.type.task_list: class: Acme\Bundle\DemoBundle\Form\TaskListType tags: - { name: form.type, alias: acme_demo_task_list }
And an example controller:
public function indexAction($page) { ini_set('xdebug.max_nesting_level', 300); // this might be useful with deeply nested forms $search = $this->getRequest()->get( 'search', array( 'name' => '', 'date' => '', 'lang' => $this->container->getParameter('acme_core.default_lang') ) ); /** * @var \Doctrine\ORM\EntityManager $em */ $em = $this->getDoctrine()->getManager(); $paginator = $this->get('knp_paginator'); $pagination = $paginator->paginate( $em->getRepository('AcmeDemoBundle:Task')->getQueryFilteringByLangNameAndDate( $search['lang'], $search['name'], $search['date'] != '' ? new \DateTime($search['date']) : null ), $page, $this->getRequest()->get('elementsPerPage', 10) ); $taskList = new TaskList(); $taskList->setFromPagination($pagination); $form = $this->createForm('acme_demo_task_list', $taskList); // "acme_demo_task_list" has been defined in the services.yml file $form->handleRequest($this->getRequest()); if ($form->isValid()) { foreach ($form->getData() as $task) { $em->merge($task); } $em->flush(); } return $this->render( 'AcmeDemoBundle:Task:index.html.twig', array( 'search' => $search, 'pagination' => $pagination, 'form' => $form->createView() ) ); }
Hope this helps!