Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass custom options to a symfony2 form

Tags:

forms

symfony

in symfony 1.4 it was possible to parameterize a form class definition, via the options of the form. Is there any way to pass custom options to my custom form type??? i've tried to use the options parameter of the buildForm method, but i'm not very sure what this array is, and apparently it is not for what i want... Thanks!

like image 811
Throoze Avatar asked Jun 06 '12 18:06

Throoze


4 Answers

The solution is simple, if you want your custom option to be available also in Twig template, you must use $builder->setAttribute() in buildForm method and $view->set() method in buildView() method, too.

<?php

namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

// For Symfony 2.1 and higher:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

/**
 * ImagePreviewType
 *
 */
class ImagePreviewType extends FormAbstractType
{

    /**
     * {@inheritDoc}
     * For Symfony 2.0
     */
    //public function getDefaultOptions(array $options)
    //{
    //    $options = parent::getDefaultOptions($options);
    //    $options['base_path'] = 'path/to/default/dir/';
    //
    //    return $options;
    //}

    /**
     * {@inheritDoc}
     * For Symfony 2.1 and higher
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'base_path'         => '',
        ));
    }

    /**
     * {@inheritDoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        // For Symfony 2.0:
        // $view->set('base_path', $form->getAttribute('base_path'));

        // For Symfony 2.1 and higher:
        $view->vars['base_path'] = $options['base_path'];
    }

    /**
     * {@inheritDoc}
     */
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->setAttribute('base_path', $options['base_path'])
        ;
    }

    /**
     * {@inheritDoc}
     */
    public function getName()
    {
        return 'image_preview';
    }

    public function getParent(array $options)
    {
        // for Symfony 2.0:
        // return 'field';

        // for Symfony 2.1 and higher:
        return 'form';
    }
}

Template for custom form type (file ...Acme/DemoBundle/Resources/views/Form/fields.html.twig):

{% block image_preview_widget %}
{% spaceless %}
<img src="{{ base_path ~ value }}" alt=""  {{ block('widget_container_attributes') }} />
{% endspaceless %}
{% endblock %}

Register your template for custom form types in app/config/config.yml

twig:
    debug:            %kernel.debug%
    strict_variables: %kernel.debug%
    form:
        resources:
            - 'AcmeDemoAdminBundle:Form:fields.html.twig'

Usage: Display preview of user's image while editing his profile:

// src/Acme/DemoBundle/Form/Type/UserType.php
namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class UserType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('user_profile_image_file_name', new ImagePreviewType(), array(
            'base_path' => 'some/other/dir',
        ));
    }
}

2014-08-18: Updated for Symfony 2.1 or higher

like image 144
pulzarraider Avatar answered Oct 16 '22 04:10

pulzarraider


UPDATE: Please note that this solution only works in Symfony 2.0.x, which is obsolete, use setDefaultOptions instead of getDefaultOptions.


Justly, Symfony 2 form types accept options that you can use for anything you want inside the form type. You need to override getDefaultOptions method to specify your type options.

For example, I have a type MyCustomType that accept my_option, this option has a default value of false, the implementation of MyCustomType can be something like this.

class MyCustomType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        if($options['my_option']){
            //do something
        } else {
            //do another thing
        }
        ...
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'my_option' => false
        );
    }

    public function getName()
    {
        return 'mycustomtype';
    }
}

Later, you will need to specify the option when you create the form in the controller, using the third parameter of buildForm:

$form = $this->buildForm(new MyCustomType(), null, array(
    'my_option' => true
));

If you not specify the my_option option, it takes the default value (false).

like image 26
eagleoneraptor Avatar answered Oct 16 '22 04:10

eagleoneraptor


Using symfony 2.8, I succeeded by using the proposed solution extending the configureOptions() method.

class ElementType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'my_custom_option_parameter' => null,
        ));
    }
}

I needed to use the ElementType, as a collection and embedded form. I recognized that it was not possible to pass the my_custom_option_parameter to the CollectionType, because I did not customize configureOptions() of CollectionType, but of my ElementType. If you need to pass the my_custom_option_parameter through a CollectionType, you may succeed by defining my_custom_option_parameter in entry_options (see Documentation CollectionType Field) array of CollectionType.

Example passing my_custom_option_parameter through a CollectionType:

class OuterFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    { 
        //...
        $builder->add('elements', CollectionType::class, array(
            'entry_type' => ElementType::class,
            // ...
            'entry_options' => array(
                'my_custom_option_parameter' => 'value is now set!'
            )
        ));
        //...
    }
}
like image 10
Erik Theoboldt Avatar answered Oct 16 '22 04:10

Erik Theoboldt


basing on @pulzarraider answer I created code with changes for Symfony 3.

You need to change

OptionsResolverInterface for OptionsResolver

FormBuilder for FormBuilderInterface

In my case:

namespace MediaBundle\Form;

use Symfony\Component\Form\AbstractType as FormAbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;

class ImageType extends FormAbstractType {

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults(array(
            'max_images' => ''
        ));
    }

    public function buildView(FormView $view, FormInterface $form, array $options) {

        $view->vars['max_images'] = $options['max_images'];
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {

        $builder
                ->setAttribute('max_images', $options['max_images'])
        ;
    }

    public function getName() {
        return 'image_preview';
    }

    public function getParent() {
        return TextareaType::class;
    }
}
like image 5
Piotrek Zatorski Avatar answered Oct 16 '22 04:10

Piotrek Zatorski