Passing data to buildForm() in Symfony 2.8, 3.0 and above

PhpSymfony

Php Problem Overview


My application currently passes data to my form type using the constructor, as recommended in this answer. However the Symfony 2.8 upgrade guide advises that passing a type instance to the createForm function is deprecated:

> Passing type instances to Form::add(), FormBuilder::add() and the > FormFactory::create*() methods is deprecated and will not be supported > anymore in Symfony 3.0. Pass the fully-qualified class name of the > type instead. > > Before:
> $form = $this->createForm(new MyType()); > > After: > $form = $this->createForm(MyType::class);

Seeing as I can't pass data through with the fully-qualified class name, is there an alternative?

Php Solutions


Solution 1 - Php

This broke some of our forms as well. I fixed it by passing the custom data through the options resolver.

In your form type:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $this->traitChoices = $options['trait_choices'];

    $builder
        ...
        ->add('figure_type', ChoiceType::class, [
            'choices' => $this->traitChoices,
        ])
        ...
    ;
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'trait_choices' => null,
    ]);
}

Then when you create the form in your controller, pass it in as an option instead of in the constructor:

$form = $this->createForm(ProfileEditType::class, $profile, [
    'trait_choices' => $traitChoices,
]);

Solution 2 - Php

Here's how to pass the data to an embedded form for anyone using Symfony 3. First do exactly what @sekl outlined above and then do the following:

In your primary FormType

Pass the var to the embedded form using 'entry_options'

->add('your_embedded_field', CollectionType::class, array(
          'entry_type' => YourEntityType::class,
          'entry_options' => array(
            'var' => $this->var
          )))

In your Embedded FormType

Add the option to the optionsResolver

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'Yourbundle\Entity\YourEntity',
        'var' => null
    ));
}

Access the variable in your buildForm function. Remember to set this variable before the builder function. In my case I needed to filter options based on a specific ID.

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $this->var = $options['var'];

    $builder
        ->add('your_field', EntityType::class, array(
          'class' => 'YourBundle:YourClass',
          'query_builder' => function ($er) {
              return $er->createQueryBuilder('u')
                ->join('u.entity', 'up')
                ->where('up.id = :var')
                ->setParameter("var", $this->var);
           }))
     ;
}

Solution 3 - Php

Here can be used another approach - inject service for retrieve data.

  1. Describe your form as service (cookbook)
  2. Add protected field and constructor to form class
  3. Use injected object for get any data you need

Example:

services:
    app.any.manager:
        class: AppBundle\Service\AnyManager

    form.my.type:
        class: AppBundle\Form\MyType
        arguments: ["@app.any.manager"]
        tags: [ name: form.type ]

<?php

namespace AppBundle\Form;

use AppBundle\Service\AnyManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MyType extends AbstractType {

    /**
     * @var AnyManager
     */
    protected $manager;

    /**
     * MyType constructor.
     * @param AnyManager $manager
     */
    public function __construct(AnyManager $manager) {
        $this->manager = $manager;
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $choices = $this->manager->getSomeData();

        $builder
            ->add('type', ChoiceType::class, [
                'choices' => $choices
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\MyData'
        ]);
    }

}

Solution 4 - Php

In case anyone is using a 'createNamedBuilder' or 'createNamed' functions from form.factory service here's the snippet on how to set and save the data using it. You cannot use the 'data' field (leave that null) and you have to set the passed data/entities as $options value.

I also incorporated @sarahg instructions about using setAllowedTypes() and setRequired() options and it seems to work fine but you first need to define field with setDefined()

Also inside the form if you need the data to be set remember to add it to 'data' field.

In Controller I am using getBlockPrefix as getName was deprecated in 2.8/3.0

Controller:

/*
* @var $builder Symfony\Component\Form\FormBuilderInterface
*/
$formTicket = $this->get('form.factory')->createNamed($tasksPerformedForm->getBlockPrefix(), TaskAddToTicket::class, null, array('ticket'=>$ticket) );

Form:

public function configureOptions(OptionsResolver $resolver)    {
    $resolver->setDefined('ticket');
    $resolver->setRequired('ticket');
    $resolver->addAllowedTypes('ticket', Ticket::class);
    
    $resolver->setDefaults(array(           
        'translation_domain'=>'AcmeForm',
        'validation_groups'=>array('validation_group_001'),
        'tasks' => null,
        'ticket' => null,
    ));
}

 public function buildForm(FormBuilderInterface $builder, array $options)   {
    
    $this->setTicket($options['ticket']);
    //This is required to set data inside the form!
    $options['data']['ticket']=$options['ticket'];

    $builder
        
        ->add('ticket',  HiddenType::class, array(
                'data_class'=>'acme\TicketBundle\Entity\Ticket',
            )
        )
...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJonathanView Question on Stackoverflow
Solution 1 - PhpseklView Answer on Stackoverflow
Solution 2 - PhpmcrieckenView Answer on Stackoverflow
Solution 3 - PhpDenisView Answer on Stackoverflow
Solution 4 - PhpEthernalView Answer on Stackoverflow