Modifying Symfony Forms html attributes / overriding a single attribute

时间秒杀一切 提交于 2019-12-07 10:56:06

问题


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?


回答1:


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.




回答2:


You might want to create a new class that resolves the nested options for you.

<?php

namespace My\AwesomeBundle\Form\Attr;

use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\Options;

class ProcedureCodeAttr
{
    /**
     * @var array
     */
    protected $options;

    /**
     * @param array $options
     */
    public function __construct(array $options = array())
    {
        $resolver = new OptionsResolver();
        $this->setDefaultOptions($resolver);

        $this->options = $resolver->resolve($options);
    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    protected function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'class'                    => 's2',
            'data-select2-placeholder' => 'Select Procedure',
        ));
    }

    /**
     * @return array
     */
    public function toArray()
    {
        return $this->options;
    }

    /**
     * @param  array $options
     * @return array
     */
    public static function resolve(array $options = array())
    {
        $attr = new static($options);

        return $attr->toArray();
    }
}

Then in your form type class you'd use it like so

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


来源:https://stackoverflow.com/questions/22058132/modifying-symfony-forms-html-attributes-overriding-a-single-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!