Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a custom service in a form type in Symfony2

In my Symfony2 project, I need to produce a list of existing tables in a db. Since not all tables are used as entities in my project, and the list itself needs to be a simple array and not an entity, I have written an sql statement for this. This sql-statement is embedded in a function in a custom service (and has been thoroughly tested).

Now, in a form type I would like to populate a field with the result array of the function mentioned above. These become the options the user can choose from.

My question is how can I access the service in my form type?

like image 485
Paul Maclean Avatar asked Jan 16 '13 10:01

Paul Maclean


4 Answers

There is a simple solution, which is pure OOP, no framework specific config. Just inject manually your service instance into your form type instance, using the constructor:

class SomeType extends AbstractType
{
    private $provider;

    public function __construct(DataBaseTableNameProvider $provider)
    {
        $this->provider = $provider;
    }

    public function buildForm(FormBuilder $builder, array $options)
    {
         $builder->add('tables', 'choices', array(
             'choices' => $this->provider->getTableNames(),
         ));
    }
}

Then, in your controller for exemple:

public function newAction()
{
     $form = $this->createForm(new SomeType($this->get('table_name_provider'));

     // more stuff
}

The exact same can be done using framework config, by following http://symfony.com/doc/2.0/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service .

The only difference is that it takes care about instanciating your type using DIC, and injecting the correct dependencies. Then, you just need to modify your controller:

public function newAction()
{
     $form = $this->createForm('some_type_alias');

     // more stuff
}
like image 51
Florian Klein Avatar answered Nov 10 '22 19:11

Florian Klein


example with translation service access in form type:

your form type class:

class TaskType extends AbstractType {
    ....
    public function setDefaultOptions(OptionsResolverInterface $resolver) {
        $resolver->setDefaults(array(
            't' => 'Symfony\Bundle\FrameworkBundle\Translation\Translator',
        ));
    }  

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $t = $options['t'];
        $t->trans('string to translate');
        ....
    }

controller:

public function someAction() { 
    $form = $this->createForm(new TaskType(), 
                                $task, 
                                array('t' => $this->get('translator')));
like image 25
meteor Avatar answered Nov 10 '22 19:11

meteor


Symfony Best Practices:

You can also register your form type as a service. This is only needed if your form type requires some dependencies to be injected by the container, otherwise it is unnecessary overhead and therefore not recommended to do this for all form type classes.

# src/AppBundle/Resources/config/services.yml
services:
    app.form.type.gender:
        class: AppBundle\Form\Type\ SomeType
        arguments:
            - "@app.table_name_provider"
        tags:
            - { name: form.type }
$form = $this->createForm(SomeType::class)
like image 3
Jonny Avatar answered Nov 10 '22 18:11

Jonny


Update for Symfony 3.X, you have two options now...

Sending the services through the options

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setRequired('option_key');
}

Then pass it through the controller

$form = $this->createForm(XXXType::class, $entity, array(
    'option_key' => $this->get('service_you_want_to_call')
));

Define your form as a service

This option is cleaner IMO, and you don't have to send the option everywhere you call the form

class XXXType extends AbstractType
{
    private $service;

    public function __construct(ServiceType $service)
    {
        $this->service = $service;
    }

And then defining the service in the config

services:
    app.form.type.XXX:
        class: AppBundle\Form\XXXType
        arguments: ['@service_you_want_to_call']
        tags:
            - { name: form.type }

Every time you call the form with

$this->createForm(XXXType::class, $data, $options)

Symfony will be smart enough to know that it has to use that service arguments (it knows it because the form.type tag), so you won't need to pass any data to the constructor anymore.

Symfony docs: How to Access Services or Config from Inside a Form

like image 1
Fernando Caraballo Avatar answered Nov 10 '22 20:11

Fernando Caraballo