2

Symfony allows you to easily customize, extend, or create your own form type widgets. This works great if you want to name your new form type 'foo'. However, if you want to name your custom type with two words like fooBar, how would you go about naming the PHP formType, the Service, and the Twig? There are plenty of examples of symfony core form types that are multiple words separated by underscore, e.g. collection_item_widget. But that doesn't work for custom form types.

So if you want to make a new form type called fooBarType, you would create a controller called fooBarType.php

class FooBarType extends AbstractType {
    public function getName() {
        return 'fooBar';
    }

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

Set this as a service (optional):

cms.form.type.foo_bar:
    class: Cms\FooBundle\Form\Type\FooBar
    tags:
        - { name: form.type, alias: fooBar }

Then customize the fields.html.twig and create a block for that named foo_bar_widget or foobar_widget or fooBar_widget (none of these work):

{% block foo_bar_widget %}
    {{ block('entity_widget') }}
{% endblock %}

NOTE: If this is in a different location, you also have to register this in config.yml

twig:
    form:
        resources:
            - 'CmsFooBundle:Form:fields.html.twig'

Then use that field in a form builder, e.g.

$builder->add('myField', 'fooBar');

Everything works above, except the custom block in the fields.html.twig does not render, unless I change all the names to something simple like 'foo'.

¿Por Que?

2 Answers 2

2

The problem with my example was in the inconsistency of the camelCase starting in the service definition, as well as a problem in the twig template.

Technically, according to specs, the service should be lowercase and underscore, e.g. cms.form.type.foo_bar. And the twig widget definitions are also underscore, e.g. collection_item_widget. So although both camelCase and underscore work (if you are consistent in the widget, the getName() of the formType controller, and in the $formBuilder->add() function) it should probably always be underscore.

This works for me now:

The Controller

class FooBarType extends AbstractType {
    public function getName() {
        return 'foo_bar';
    }

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

The Service Definition

cms.form.type.foo_bar:
    class: Cms\FooBundle\Form\Type\FooBar
    tags:
        - { name: form.type, alias: foo_bar }

The Usage in the Form Builder

$builder->add('myField', 'foo_bar');

The fields.html.twig The fields.html.twig can be defined as camelCase (to match the alias). But there is no such thing as block('entity_widget') evidently. I think entity is just an extension of the choice widget. So my custom widget was loading, but no choices were present because that block was incorrect. It works if I change this to choice_widget:

{% block foo_bar_widget %}
    <!-- some customizations -->
    {{ block('choice_widget') }}
{% endblock %}
Sign up to request clarification or add additional context in comments.

Comments

1

Just a note ahead: You don't have to define the form type as a service, if you don't have any parameters or other services you need to inject into the type. You can always call the form type directly in the form builder via:

$builder
    ->add('foo_bar_field', new FooBarType());

Anyway, I just did a quick test using the gender type example from the documentation and it worked fine for me.

These are the settings i used:

Type class Jahller\TestBundle\Form\Type\GenderDoType.php

namespace Jahller\TestBundle\Form\Type;

// ...

class GenderDoType extends AbstractType
{
    // ...

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

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

Service definition Jahller/TestBundle/Resources/config/services.yml

jahller.form.type.gender:
        class: Jahller\TestBundle\Form\Type\GenderDoType
        tags:
            - { name: form.type, alias: genderDo }

Type twig template Jahller/TestBundle/Resources/views/Form/fields.html.twig

{% block genderDo_widget %}
    {% spaceless %}
        {% if expanded %}
            <ul {{ block('widget_container_attributes') }}>
                {% for child in form %}
                    <li>
                        {{ form_widget(child) }}
                        {{ form_label(child) }}
                    </li>
                {% endfor %}
            </ul>
        {% else %}
            {# just let the choice widget render the select tag #}
            {{ block('choice_widget') }}
        {% endif %}
    {% endspaceless %}
{% endblock %}

Form class with type include Jahller\TestBundle\Form\TestType.php

namespace Jahller\TestBundle\Form;

// ...

class TestType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('gender_code', 'genderDo', array(
                'placeholder' => 'Choose a gender',
            ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        // ...   
    }

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

Type rendered:

enter image description here

Maybe you can find something in my code you did differently.

2 Comments

Thanks. The problem I had was with the double word, not knowing whether to do underscores or camelCase. But I see you did camelCase genderDo_widget and it worked, so that's great confirmation (it's not mentioned in the documentation so hopefully that helps others). Maybe when I was testing various options I had combination wrong, e.g. defining the service as foo_bar instead of fooBar (as my example shows). I'll have to try again.
I found the problem (and you avoided it in your example by calling your service jahller.form.type.gender instead of jahller.form.type.gender_do). The problem was caused by me using an underscore in the service name, when for consistency I guess it has to be camelCase jahller.form.type.genderDo (although I thought that wasn't allowed).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.