Symfony: How to avoid custom form-types getting wrapped in a div automatically?

By default, in the base form theme:

{% block form_row %}
{% spaceless %}
    <div>
        {{ form_label(form) }}
        {{ form_errors(form) }}
        {{ form_widget(form) }}
    </div>
{% endspaceless %}
{% endblock form_row %}

And, for custom compound forms:

{% block form_widget_compound %}
{% spaceless %}
    <div {{ block('widget_container_attributes') }}>
        {% if form.parent is empty %}
            {{ form_errors(form) }}
        {% endif %}
        {{ block('form_rows') }}
        {{ form_rest(form) }}
    </div>
{% endspaceless %}
{% endblock form_widget_compound %}

Unless you changed something here, the DIV you see should come from either one or the other bit of template.

However, in your specfic example, if form_tutor_user_row is defined, the first bit is never used, and if form_tutor_user_widget is defined, the last bit is never used.

Back to your question. Your question is : "How can i change the theme so all custom types are not wrapped with the default form_row template?"

Here is the problem the way I see it: you want that your TOP forms (the form in which all sub-forms are included) all have a common way of rendering, in sections. Each section will be included in a DIV with class="form-group". You may want to throw in some additional rendering operations but I will limit myself to this to keep things simple.

What you need to do then is to create a specfic form type and make all your TOP forms inherit from this new form type. For instance:

class TopType extends AbstractType
{
    public function getName()
    {
        return 'top_form';
    }
}

... and an inherited form:

class MyFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        ...
    }

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

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

As you can see, there is no need to make PHP inheritance for form theming inheritance to work.

Template-theming-wise (can I even say that?), if no specific form theming is set for my_form, Symfony will understand that the default form theme to use here is the form theme of top_form, that you can define as such:

{% block top_form_widget %}
{% spaceless %}
    {% for child in form %}
        <div class="form-group">
            {{ form_widget(child) }}
        </div>
    {% endfor %}
    {{ form_rest(form) }}
{% endspaceless %}
{% endblock top_form_widget %}

I should add that this is a problem I already encountered and solved. Tell me how that works for you.

Edit:

To sum it up, what you have to do is:

  • Create the TopType form type,
  • Add the top_form_widget block in your form theme,
  • For all your root forms (i.e. top-level forms, forms that have no parent), add a getParent() method that will return the name of your TopType form ("top_form")

In theory, if you override the form_widget_compound block in a global form theme this way, it should work as you want:

// app/Resources/views/form.html.twig

{% block form_widget_compound %}
    {% if form.parent is empty %}
        <div {{ block('widget_container_attributes') }}>
        {{ form_errors(form) }}
    {% endif %}
    {{ block('form_rows') }}
    {{ form_rest(form) }}
    {% if form.parent is empty %}
        </div>
    {% endif %}
{% endblock %}

And register your form theme:

// app/config/config.yml
twig:
    form:
        resources:
            - "::form.html.twig"

I usually solve this problem by rendering the nested form's fields manually:

{{ form_row(form.tutor.school) }}
{{ form_row(form.tutor.user.email) }}

Probably that's not the most elegant solution, but it works for me and I haven't looked for an elegant one yet.