Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying Symfony Forms html attributes / overriding a single attribute

I am trying to figure out how to modify html attributes on the fly with Symfony2 forms.

The situation is a case where a default placeholder is used most of the time, but occasionally, the developer needs to write a custom message.

My Form type looks like this:

    <?php

    namespace My\AwesomeBundle\FormType;

    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;
    use My\AwesomeBundle\Transformer\ProcedureCodeTransformer;

    class ProcedureType extends AbstractType
    {

        private $em;
        private $user;


        public function __construct($em, $securityContext)
        {
            $this->em=$em;
            $this->user=$securityContext->getToken()->getUser();
        }

        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $transformer = new ProcedureTransformer( $this->em );
            $builder->addModelTransformer( $transformer );
        }

        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $user = $this->user;
            $resolver->setDefaults(
                array(
                    'class'       => 'My\AwesomeBundle\Entity\Procedure',
                    'label'       => 'Procedure',
                    'label_attr'  => array( 'class'=> 'control-label' ),
                    'required'    => false,
                    'empty_value' => '',

                    'attr'        => array(
                        'class'                    => 's2',
                        'data-select2-placeholder' => 'Select Procedure',
                    ),
                )
            );

            $resolver->setOptional( array( 'placeholder' ) );
        }

        public function getParent() {
            return 'hidden';
        }

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

The default render then has "Select Procedure" for the data-select2-placeholder element and javascript is used to display it. This is then used in a more complex type:

    <?php

    namespace My\AwesomeBundle\FormType;

    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolverInterface;

    class ProcedureType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options){
            $builder->add('biller', 'billers', array( 'placeholder' => 'New Text'));

            [...]

        }

        public function setDefaultOptions(OptionsResolverInterface $resolver){
            $resolver->setDefaults( array(
                    'data_class' => 'My\AwesomeBundle\Entity\Procedure'
                ) );
        }

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

I would like 'New Text' to be placed into the data-select2-placeholder html attribute. However, if I call the builder like this:

    $builder->add('procedure',
        new ProcedureCodeType()),
        array( 'attr' => array('data-select2-placeholder' => 'New Text') ) 
    );

The entire html attribute array is replaced. this is not surprising. Is there a function in the form builder that has eluded me to add or modify a single html attribute?

like image 787
Lighthart Avatar asked Jan 12 '23 03:01

Lighthart


1 Answers

Unfortunately, there is no way to modify a single attr the way you would want it. This is the closest and most simple solution I could come with:

What you have to do in your situation is to use callback functions in your options.

For each default attribute in your form types, instead of a normal value, you can use a callback function that accepts a single input representing your options (Symfony class Symfony\Component\OptionsResolver\Options). The function is called when building the form and the return value is then used as the value of the option.

That way, you can build the resulting option depending on the other provided options. An example:

use Symfony\Component\OptionsResolver\Options;

class ProcedureType extends AbstractType
{
    ...

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $attr = function(Options $options) {
            $default = array(
                'class'                    => 's2',
                'data-select2-placeholder' => 'Select Procedure',
            );
            return array_replace($default, $options['new_attr']);
        };

        $user = $this->user;
        $resolver->setDefaults(
            array(
                'class'       => 'My\AwesomeBundle\Entity\Procedure',
                'label'       => 'Procedure',
                'label_attr'  => array( 'class'=> 'control-label' ),
                'required'    => false,
                'empty_value' => '',
                'attr'        => $attr,
                'new_attr'    => array(),
            )
        );

        $resolver->setOptional( array( 'placeholder' ) );
    }

That way, what you have to modify will be new_attr.

like image 117
Zephyr Avatar answered Jan 16 '23 22:01

Zephyr