Je voudrais rendre un arbre avec une profondeur indéterminée (enfants d'enfants d'enfants, etc.). J'ai besoin de parcourir le tableau de manière récursive; comment puis-je faire cela dans Twig?
Réponses:
J'ai joué avec l'idée de domi27 et j'ai trouvé ça. J'ai créé un tableau imbriqué comme mon arbre, ['link'] ['sublinks'] est nul ou un autre tableau de plus de la même chose.
Modèles
Le fichier de sous-modèle avec lequel récurer:
<!--includes/menu-links.html-->
{% for link in links %}
    <li>
        <a href="{{ link.href }}">{{ link.name }}</a>
        {% if link.sublinks %}
            <ul>
                {% include "includes/menu-links.html" with {'links': link.sublinks} %}
            </ul>
        {% endif %}
    </li>
{% endfor %}
Ensuite, dans le modèle principal, appelez ceci (sorte de trucs redondants 'avec'):
<ul class="main-menu">
    {% include "includes/menu-links.html" with {'links':links} only %}
</ul>
Macros
Un effet similaire peut être obtenu avec des macros:
<!--macros/menu-macros.html-->
{% macro menu_links(links) %}
    {% for link in links %}
        <li>
            <a href="{{ link.href }}">{{ link.name }}</a>
            {% if link.sublinks %}
                <ul>
                    {{ _self.menu_links(link.sublinks) }}
                </ul>
            {% endif %}
        </li>
    {% endfor %}
{% endmacro %}
Dans le modèle principal, procédez comme suit:
{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>
{{_self.menu_links}}est une mauvaise pratique ! Lisez une note ici: macro   Lorsque vous définissez une macro dans le modèle où vous allez l'utiliser, vous pourriez être tenté d'appeler la macro directement via _self.input () au lieu de l'importer; même si cela semble fonctionner, ce n'est qu'un effet secondaire de l'implémentation actuelle et cela ne fonctionnera plus dans Twig 2.x. Vous devez à nouveau importer des macros localement sur placemenu_links
                    Si vous souhaitez utiliser une macro dans le même modèle , vous devez utiliser quelque chose comme ceci pour rester compatible avec Twig 2.x :
{% macro menu_links(links) %}
    {% import _self as macros %}
    {% for link in links %}
        <li>
            <a href="{{ link.href }}">{{ link.name }}</a>
            {% if link.sublinks %}
                <ul>
                    {{ macros.menu_links(link.sublinks) }}
                </ul>
            {% endif %}
        </li>
    {% endfor %}
{% endmacro %}
{% import _self as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>
Ceci étend random-coderla réponse et incorpore l dr.scre'astuce de la documentation Twig sur les macros à utiliser maintenant _self, mais à importer localement.
Depuis Twig 2.11 , vous pouvez omettre le {% import _self as macros %}, car les macros intégrées sont importées automatiquement sous l' _selfespace de noms (voir Annonce Twig: importation automatique de macro ):
{# {% import _self as macros %} - Can be removed #}
<ul class="main-menu">
    {{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>
Si vous utilisez PHP 5.4 ou supérieur, il existe une merveilleuse nouvelle solution (à partir de mai 2016) à ce problème par Alain Tiemblo: https://github.com/ninsuo/jordan-tree .
C'est une balise "arbre" qui sert exactement cet objectif. Le balisage ressemblerait à ceci:
{% tree link in links %}
    {% if treeloop.first %}<ul>{% endif %}
    <li>
        <a href="{{ link.href }}">{{ link.name }}</a>
        {% subtree link.sublinks %}
    </li>
    {% if treeloop.last %}</ul>{% endif %}
{% endtree %}
subtree. Dans mon cas, le code a besoin de savoir s'il y aura plus d'enfants et il passe le nombre de niveaux à la macro afin qu'elle puisse faire un <div class="{{ classes[current_level].wrapper }} {% if levels > current_level %}accordion-wrapper{% endif %}">. Pour calculer cela, il faudrait répéter le niveau actuel une seconde fois pour déterminer s'il y a des enfants.
                    J'ai d'abord pensé que cela pouvait être résolu de manière simple, mais ce n'est pas si simple.
Vous devez créer une logique, peut-être avec une méthode de classe PHP, quand inclure un sous-modèle Twig et quand non.
<!-- tpl.html.twig -->
<ul>
    {% for key, item in menu %}
        {# Pseudo Twig code #}
        {% if item|hassubitem %}
            {% include "subitem.html.tpl" %}
        {% else %}
            <li>{{ item }}</li>
        {% endif %}
    {% endfor %}
</ul>
Vous pouvez donc utiliser la variable de boucle Twig spéciale , qui est disponible dans une boucle Twig for . Mais je ne suis pas sûr de la portée de cette boucle variable de .
Ceci et d'autres informations sont disponibles sur Twigs "pour" Docu !
A pris la réponse de la grippe et l'a modifiée un peu:
{# Macro #}
{% macro tree(items) %}
    {% import _self as m %}
        {% if items %}
        <ul>
            {% for i in items %}
                <li>
                    <a href="{{ i.url }}">{{ i.title }}</a>
                    {{ m.tree(i.items) }}
                </li>
            {% endfor %}
        </ul>
    {% endif %}
{% endmacro %}
{# Usage #}
{% import 'macros.twig' as m %}
{{ m.tree(items) }}
Les réponses ici mènent à ma solution.
J'ai une entité de catégorie avec une association plusieurs-à-un auto-référencée (parent-enfant).
/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
 */
private $parent;
/**
 * @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
 */
private $children;
Dans mon modèle Twig, je rend l'arborescence comme ceci:
<ul>
{% for category in categories %}
    {% if category.parent == null %}
        <li>
            <a href="{{ category.id }}">{{ category.name }}</a>
            {% if category.children|length > 0 %}
            <ul>
            {% for category in category.children %}
                <li>
                    <a href="{{ category.id }}">{{ category.name }}</a>
                </li>
            {% endfor %}
            </ul>
            {% endif %}
        </li>
    {% endif %}
{% endfor %}
</ul>
{{ _self.menu_links(links) }}.