Question

I was sure I understand this process, but when I dug deeper I saw I am wrong ;(

Let's take simple form, please notice that this form contains 3 fields

    $form = $this->createFormBuilder($defaultData, ['csrf_protection' => false])
            ->add('email', 'email')
            ->add('name', 'text')
            ->add('message', 'textarea')
            ->getForm()
            ->createView();

which is rendered as

{{ form(form, {'attr': {'novalidate': 'novalidate'}}) }}

into

enter image description here

Built in "form" block from vendor\symfony\symfony\src\Symfony\Bridge\Twig\Resources\views\Form\form_div_layout.html.twig looks like:

{% block form %}
{% spaceless %}
    {{ form_start(form) }}
        {{ form_widget(form) }}
    {{ form_end(form) }}
{% endspaceless %}
{% endblock form %}

and form_widget(form)

{% block form_widget %}
{% spaceless %}
    {% if compound %}
        {{ block('form_widget_compound') }}
    {% else %}
        {{ block('form_widget_simple') }}
    {% endif %}
{% endspaceless %}
{% endblock form_widget %}

{% block form_widget_simple %}
{% spaceless %}
    {% set type = type|default('text') %}
    <input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endspaceless %}
{% endblock form_widget_simple %}

Let's modify form and form_widget a bit:

{% block form %}
{% spaceless %}
    {{ form_start(form) }}
        -form_widget before-
        {{ form_widget(form) }}
        -form_widget after-
    {{ form_end(form) }}
{% endspaceless %}
{% endblock form %}

{% block form_widget %}
{% spaceless %}
    - form_widget call -
    {% if compound %}
        - form_widget compound-
        {{ block('form_widget_compound') }}
    {% else %}
        - form_widget simple-
        {{ block('form_widget_simple') }}
    {% endif %}
{% endspaceless %}
{% endblock form_widget %}

then as an output I got (listing 5):

<form name="form" method="post" action="" novalidate="novalidate">
    -form_widget before-
    - form_widget call -
    - form_widget compound-
    <div id="form" novalidate="novalidate">
        <div>
            <label for="form_email" class="required">Email</label>
            <input type="email" id="form_email" name="form[email]" required="required" />
        </div>
        <div>
            <label for="form_name" class="required">Name</label>
            - form_widget call -
            - form_widget simple-
            <input type="text" id="form_name" name="form[name]" required="required" />
        </div>
        <div>
            <label for="form_message" class="required">Message</label>
            <textarea id="form_message" name="form[message]" required="required">Type your message here</textarea>
        </div>
    </div>
    -form_widget after-
</form>

we can easly notcie the flow

form -> form_widget (input parameter is the whole form) -> form_widget_compound -> form_rows (iterates form elements into next function) -> form_row -> form_widget (this call form element is passed as parameter)

so here is the time for question, if form_widget is called 4 times (or more), once for form, 3 times for fields then why in listing 5 'form_widget call' text appears only 2 times? or other words how email and message were rendered?

Was it helpful?

Solution

The form theming in SF2 uses inheritance. That means that if blocks named email_widget and textarea_widget are defined, neither your e-mail nor your textarea fields will use form_widget. They will rather respectively use their own widget blocks: email_widget and textarea_widget.

Well, in form_div_layout.twig, these two widget blocks are defined. Thus form_widget is not called for 2 of your fields. Thus your message is displayed 2 times instead of 4.

If you want to customize the rendering of these fields, you will have to create your own block definitions email_widget and textarea_widget in your custom form theme file.

Edit:

The default form theme files are defined under Symfony\Bridge\Twig\Resources\views\Form. The default file used is form_div_layout.html.twig.

Although the inheritance logic itself is indeed in the PHP code of the TWIG bridge, it does not define which block inherit from which other block.

Inheritance is actually defined in your FormType classes. Each form type class sports a getParent() method that return the name of the form from which it inherits. The name of a form type is the result of the method getName() of its associated form type class. For instance, with a built-in example, Symfony\Component\Form\Extension\Core\Type\TextareaType:

Textarea >> Text >> Field >> Form

All you have to do is to look for the methods getParent() and getName(). Thus, by default, when rendering the textarea row, Twig will search the block textarea_row, text_row, field_row and finally form_row (the base default row). The first of these blocks that is defined in your form theme is rendered.

The definition of the blocks themselves happen in the form theme files.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top