DrKey DrKey - 6 months ago 393
Twig Question

Twig - Iterating over Form fields

I'm facing a problem trying to iterate over all form elements in a Twig form theme. Actually I used this to iterate over them:

{% for child in form.parent.children %}


I didn't found it in documentation, I just dumped form variable and found the fields on that path. This actually works well with all forms except when the form itself has a parameter called children. I don't know if it's a bug because if you just dump
form
object the structure is the same in both cases:

enter image description here

But if you try to access
form.parent.children
when it has a
children
parameter inside you won't get that array rather directly the result of
"children"
:

enter image description here

As you can see in this case
form.parent.children
refers directly to form's children element (same identifier #1592). Still if you try to get
form.parent.children.parent.children
you will get again
children
element so basically using this way is not possible to iterate over form fields if form contains a parameter called
children
.

Is it a bug or am I missing something? Maybe there's another way to achieve what I want?

Answer Source

Yes, it's a name collision related to the way Twig accesses each attribute for convenience and because FormView is declared as \ArrayAccess it has priority over object's properties. By the way, the same would happen with parent and vars properties, but let's focus on the solution now rather than on the problem.

As it is a Twig's issue the solution should be targeted on this direction. A workaround could be create a custom function that obtains the property of the FormView correctly:

public function getFunctions()
{
    return array(
        new TwigFunction('formview_prop', array($this, 'getFormViewProperty')),
    );
}

public function getFormViewProperty(FormView $formView, string $prop)
{
    // parent, children or vars
    return $formView->{$prop};
}

So, when using this function you can access to the view's properties rather than form's fields (if there is a name collision):

{% for child in formview_prop(form, 'parent') %}

Then, it will iterate over all field elements (children) of the parent view. However, I'd prefer be explicit by creating three functions formview_parent, formview_children and formview_vars instead and don't pass the second parameter.


In this sense, you might need verify also whether the form has a parent view or not, so since Symfony 2.7.39, 2.8.32, 3.3.14, 3.4.1, 4.0.1 was introduced (as bugfix) a new Twig's test function named rootform that avoid this kind of collision, especially for parent property:

{% if form is rootform %}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download