Nitseg
Nitseg

Reputation: 1277

Extend the ChoiceType to get a new option in a form

In a form I need to associate each radio button of an input to a different image.

I would call the construction of this form this way:

$builder = $this->formFactory->createBuilder();

$builder->add('input_name', 'my_choice', array(
    'data' => 'n',
    'choices' => array('c1' => 'choice1', 'c2' => 'choice2'),
    'required' => true,
    'expanded' => true,
    'multiple' => false,
    'images' => array('choice1.jpg', 'choice2.jpg')));

$form = $builder->getForm();

Meaning the radio_button choice1 will be associated to choice1.jpg and the radio_button choice2 will be associated to choice2.jpg.

In plaint HTML using Bootstrap I want this result

<form method="post" action="{{ path('my_path')}}" name="myform">
    <div class="btn-group btn-group-justified" data-toggle="buttons">

        <label id="btn-choice1" class="btn active">
           <input type="radio" name="is_choice" id="c1" value="Choice1" checked>
               <img   id="choice1_img" src="{{ asset('bundles/myBundle/icons/choice1.png') }}" alt="Choice 1">                                            
        </label>

        <label id="btn-choice2" class="btn">
           <input type="radio" name="is_choice" id="c2" value="Choice2"><img id="choice2_img" src="{{ asset('bundles/myBundle/icons/choice2.png') }}" alt="Choice 2">
        </label>
    </div>
    <button class="btn" type="submit" name="submit">Submit</button>
</form>

I read the official Symfony doc related to this: http://symfony.com/fr/doc/2.3/cookbook/form/create_custom_field_type.html but the example is very poor.

I need to describe to Symfony where to look for this new option but cannot figure out how to achieve this. I read hundreds of examples on the net but nothing explaining the logic behind how to do this.

So far My new Type is like this below:

<?php

namespace myProject\Bundle\MyBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

class MyChoiceType extends AbstractType
{

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars = array_replace($view->vars, array(
            'images' => $options['images']
            ));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'images' => array()
            ));
    }

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

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

Would you be able to explain me how to describe this new Type to Symfony (what methods to call with what parameters).

I breaked the code for hours but Symfony is making so muchfor a simple form creation that I lost the path. FormRegistry, FormType, OptionsResolverInterface, BaseType, FormFactory and so many others classes are involved that it makes it difficult to get the global picture.

Otherwise if one can point me to an architecture document explaining the logical behind this it could also be fine.

Upvotes: 1

Views: 809

Answers (1)

Nitseg
Nitseg

Reputation: 1277

Found the solution.

I needed also to access the images new option through a Form theme redefinition. That was the difficult part since I didn't know how and what I could access from the form theme template.

Here is how I succeeded in doing it:

{% block my_choice_widget %}
    {% spaceless %}
        {% if expanded %}

            <ul {{ block('widget_container_attributes') }}>
            {% for child in form %}
                <li>
                    {{ form_widget(child) }} 
                    {{ form_label(child) }}
                    {% if images is defined %} {{images[loop.index-1]}} {% endif %}
                </li>
            {% endfor %}            
            </ul>
        {% else %}
            {# just let the choice widget render the select tag #}
            {{ block('choice_widget') }}
        {% endif %}
    {% endspaceless %}
{% endblock %}

The part I added to the Symfony form them template is:

{% if images is defined %} {{images[loop.index-1]}} {% endif %}

That displays the image name in front of the proper radio button. That should be refined to make sure there are enough images to populate all the loops. A simple twig test on images count compared to loop.last element will make it.

Upvotes: 1

Related Questions