Jak wyrenderować drzewo w Twig

90

Chciałbym wyrenderować drzewo o nieokreślonej głębokości (dzieci dzieci itp.). Muszę cyklicznie przechodzić przez tablicę; jak mogę to zrobić w Twig?

T-RonX
źródło

Odpowiedzi:

117

I bawił się pomysłem domi27 męska i wpadł na to. Zrobiłem zagnieżdżoną tablicę jako moje drzewo, ['link'] ['podrzędne'] ma wartość null lub inną tablicę, która zawiera więcej takich samych.

Szablony

Plik pod-szablonu do ponownego wykorzystania:

<!--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 %}

Następnie w głównym szablonie nazwij to (rodzaj zbędnych rzeczy tam „z”):

<ul class="main-menu">
    {% include "includes/menu-links.html" with {'links':links} only %}
</ul>

Makra

Podobny efekt można osiągnąć za pomocą makr:

<!--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 %}

W głównym szablonie zrób to:

{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>
koder losowy-1920
źródło
9
Bardzo dobrze, dziękuję! Jeśli chcesz użyć makra w tym samym szablonie, możesz użyć {{ _self.menu_links(links) }}.
grypa
dziękuję, myśl o tym spowodowała ból w moim mózgu, ale twoja odpowiedź ma sens.
azzy81
Miałem jeden problem z moim projektem z komentarzami. podkomentarze (podrzędne) były również zawarte w kolekcji głównej (linki). więc przed dołączeniem musiałem sprawdzić, czy komentarz zawierał wpis „rodzica”.
Jevgeni Smirnov
4
Używanie {{_self.menu_links}}to zła praktyka ! Przeczytaj notatkę tutaj: makro Gdy definiujesz makro w szablonie, w którym zamierzasz go użyć, możesz ulec pokusie wywołania makra bezpośrednio przez _self.input () zamiast importowania go; nawet jeśli wydaje się działać, jest to tylko efekt uboczny obecnej implementacji i nie będzie już działać w Twig 2.x. Powinieneś ponownie zaimportować makra lokalnie insitemenu_links
dr.scre
37

Twig 2.0 - 2.11

Jeśli chcesz użyć makra w tym samym szablonie , powinieneś użyć czegoś takiego, aby pozostać kompatybilnym z 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>

To rozszerza random-coderodpowiedź i włącza dr.screwskazówkę do dokumentacji Twig na temat makr, które mają być teraz używane _self, ale importowane lokalnie.

Gałązka> = 2,11

Począwszy od Twig 2.11 , możesz pominąć {% import _self as macros %}, ponieważ wbudowane makra są importowane automatycznie w _selfprzestrzeni nazw (zobacz ogłoszenie Twig: Automatyczny import makr ):

{# {% import _self as macros %} - Can be removed #}

<ul class="main-menu">
    {{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>
grypa
źródło
2

Jeśli korzystasz z PHP 5.4 lub nowszego, istnieje wspaniałe nowe rozwiązanie (stan na maj 2016 r.) Autorstwa Alaina Tiemblo: https://github.com/ninsuo/jordan-tree .

Jest to tag „drzewo”, który służy właśnie do tego celu. Znaczniki wyglądałyby następująco:

{% 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
źródło
1
Nie możesz przekazać dodatkowych zmiennych do subtree. W moim przypadku kod musi wiedzieć, czy będzie więcej dzieci i przekazuje liczbę poziomów do makra, aby mogło wykonać plik <div class="{{ classes[current_level].wrapper }} {% if levels > current_level %}accordion-wrapper{% endif %}">. Obliczenie tego wymagałoby powtórnego powtórzenia bieżącego poziomu, aby uchwycić, czy są jakieś dzieci.
chx
1

Najpierw pomyślałem, że można to rozwiązać w prosty sposób, ale nie jest to takie łatwe.

Musisz stworzyć logikę, być może za pomocą metody klasy PHP, kiedy dołączyć podszablon Twig, a kiedy nie.

<!-- 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>

Możesz więc użyć specjalnej zmiennej pętli Twig , która jest dostępna wewnątrz pętli Twig for . Ale nie jestem pewien co do zakresu tej zmiennej pętli .

Te i inne informacje są dostępne na Twigs „for” Docu !

domi27
źródło
1

Wziąłem odpowiedź grypy i trochę ją zmodyfikowałem:

{# 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
źródło
-1

Odpowiedzi tutaj prowadzą mnie do mojego rozwiązania.

Mam jednostkę kategorii z odwołującym się do siebie skojarzeniem wiele do jednego (rodzic do dzieci).

/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
 */
private $parent;

/**
 * @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
 */
private $children;

W moim szablonie Twig renderuję widok drzewa w następujący sposób:

<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
źródło
A jeśli masz więcej niż jeden poziom hierarchii kategorii?
puszczono