7

I have the todolist where i display three forms of task type

$task1 = new Task();
$form1 = $this->createForm(new MyForm('f1'), $task1);

$task2 = new Task('fo');
$form2 = $this->createForm(new MyForm('f2'), $task2);

$task3 = new Task();
$form3 = $this->createForm(new MyForm('f3'), $task3);

Now the problem is i have one submit button only . How can i persist these three tasks within one controller. and user can add more forms dynamically as well.

so what the way to solve this

2
  • You should think about using jQuery for such tasks. It allows you submission of all forms. Commented Aug 1, 2012 at 8:04
  • Buy what has jquery to do with doctrine and persisting. Can you give me some link so that i can see what you mean Commented Aug 1, 2012 at 8:12

3 Answers 3

5

Create a Form Model class — like TaskList — that holds a collection of Tasks. Then create TaskListType that holds a collection of TaskTypes. This way you'll have one form with as many tasks as you want.

Sign up to request clarification or add additional context in comments.

Comments

5

For the sake of completeness find below a complete example.

You should create a new Model that represents the desired form. The point is that you probably don't want to affect Doctrine (eg. see doctrine:schema:update command). It might try to create a table for an entity that doesn't really exist. To avoid that, just put your model class under the Model folder (\src\Acme\Bundle\DemoBundle\Model\TaskList.php).

Assume that the following is your TaskType form class:

<?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
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('id', null, array('read_only' => true))
            ->add('name');
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'Acme\Bundle\DemoBundle\Entity\Task'
            )
        );
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'acme_demo_task';
    }
}

This should be your TaskList model class:

<?php

namespace Acme\Bundle\DemoBundle\Model;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Class TaskList
 * @package Acme\Bundle\DemoBundle\Model
 *
 * @ORM\Entity()
 */
class TaskList
{
    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     * @ORM\ManyToMany(targetEntity="\Acme\Bundle\DemoBundle\Entity\Task")
     */
    private $tasks;

    public function __construct()
    {
        $this->tasks = new ArrayCollection();
    }

    /**
     * @param \Acme\Bundle\DemoBundle\Entity\Task $task
     * @return $this
     */
    public function addTask(\Acme\Bundle\DemoBundle\Entity\Task $task)
    {
        $this->tasks[] = $task;

        return $this;
    }

    /**
     * @param \Acme\Bundle\DemoBundle\Entity\Task $task
     * @return $this
     */
    public function removeTask(\Acme\Bundle\DemoBundle\Entity\Task $task)
    {
        $this->tasks->remove($task);

        return $this;
    }

    /**
     * @return ArrayCollection
     */
    public function getTasks()
    {
        return $this->tasks;
    }

    /**
     * @param \Doctrine\Common\Collections\Collection $tasks
     * @return $this
     */
    public function setTasks(\Doctrine\Common\Collections\Collection $tasks)
    {
        $this->tasks = $tasks;

        return $this;
    }

    /**
     * @param \Knp\Component\Pager\Pagination\PaginationInterface $pagination
     * @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
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(
                'tasks',
                'collection',
                array(
                    'type' => new \Acme\Bundle\DemoBundle\Form\TaskType(),
                )
            )
            ->add('save', 'submit');
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'Acme\Bundle\DemoBundle\Model\TaskList'
            )
        );
    }

    /**
     * @return string
     */
    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 a sample 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()
        )
    );
}

I hope this helps!

Comments

4

We followed the exapmle shown by 'Francesco Casula' and it worked perfectly.

For orur purposes we didn't need pagination, so this is how we filled our collection (in the controller):

$entities = $em->getRepository('AcmeBundle:Stock')->findAll();

$stockList = new StockList(); // This is our model, what Francesco called 'TaskList'

foreach ($entities as $entity) {
    $stockList->addStock($entity);
}

// StockListType() is what Francesco called TaskListType
$form = $this->createForm(new StockListType(), $stockList, array(
    'action' => $this->generateUrl('stock_take_update'),
    'method' => 'POST',
    'attr' => array('class' => 'form-horizontal'),
));

For those who needs to customise the output of the form collections, we managed to access the subform by iterating on {% for form in forms.children.stocks %}. 'Stocks' being the name of the field in our form builder (in StockListType):

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add(
            'stocks', 'collection', array(
                'type' => new StockType(),
            )
        )
        ->add('submit', 'submit')
    ;
}

This is what we ended up using in our twig view:

{{ form_start(forms) }}
    <table class="table table-stripped table-hover">
        <thead>
            <th>#</th>
            <th>Name</th>
            <th>Code</th>
            <th>Location</th>
            <th>Total</th>
            <th>Updated Toal</th>
        </thead>
        <tbody>
            {% set counter = 1 %}
            {% for form in forms.children.stocks %}
                <tr>
                    <div class="hidden">
                        {{ form_widget(form.name) }}
                        {{ form_widget(form.code) }}
                        {{ form_widget(form.location) }}
                        {{ form_widget(form.available) }}
                        {{ form_widget(form.assigned) }}
                        {{ form_widget(form.minLevel) }}
                        {{ form_widget(form.type) }}
                        {{ form_widget(form.colourCode) }}
                    </div>
                    <td>{{ counter }}</td>
                    <td>
                        {% if form.vars.data.name is defined %}
                            {{ form.vars.data.name }}
                        {% endif %}
                    </td>
                    <td>
                        {% if form.vars.data.code is defined %}
                            {{ form.vars.data.code }}
                        {% endif %}
                    </td>
                    <td>
                        {% if form.vars.data.location is defined %}
                            {{ form.vars.data.location }}
                        {% endif %}
                    </td>
                    <td>
                        {% if form.vars.data.total is defined %}
                            {{ form.vars.data.total }}
                        {% endif %}
                    </td>
                    <td>{{ form_widget(form.total) }}</td>
                </tr>
                {% set counter = counter + 1 %}
            {% endfor %}
        </tbody>
    </table>
    {{ form_widget(forms.submit) }}
{{ form_end(forms) }}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.