Comment rendre un arbre dans Twig

89

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?

T-RonX
la source

Réponses:

117

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>
codeur-aléatoire-1920
la source
9
Très bien merci! Si vous souhaitez utiliser la macro dans le même modèle, vous pouvez utiliser {{ _self.menu_links(links) }}.
grippe
merci, cette pensée m'a fait mal au cerveau, mais votre réponse est parfaitement logique.
azzy81
J'ai eu un problème avec mon projet avec des commentaires. des sous-commentaires (sous-liens) ont également été inclus dans la collection principale (liens). donc avant d'inclure j'ai dû vérifier si le commentaire avait une entrée «parent».
Jevgeni Smirnov
4
L'utilisation {{_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
dr.scre
35

Twig 2.0 - 2.11

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.

Brindille> = 2,11

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>
grippe
la source
2

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 %}
Jordan Lev
la source
1
Vous ne pouvez pas transmettre de variables supplémentaires à 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.
chx
1

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 !

domi27
la source
0

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) }}
Sergey Atroshchenko
la source
-1

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>
Patric Robert Gutersohn
la source
Que faire si vous avez plus d'un niveau de hiérarchie de catégories?
pmoubed le