Как отрендерить дерево в Twig

90

Я хотел бы визуализировать дерево с неопределенной глубиной (дочерние элементы детей и т. Д.). Мне нужно рекурсивно перебрать массив; как я могу сделать это в Twig?

T-RonX
источник

Ответы:

117

Я поигрался с идеей domi27 и придумал это. Я сделал вложенный массив в качестве своего дерева, ['link'] ['sublinks'] имеет значение null или другой массив того же самого.

Шаблоны

Файл подшаблона для рекурсии:

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

Затем в основном шаблоне назовите это (что-то вроде лишнего 'with' там):

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

Макросы

Подобного эффекта можно добиться с помощью макросов:

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

В основном шаблоне сделайте так:

{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>
случайный кодер-1920
источник
9
Очень хорошо Спасибо! Если вы хотите использовать макрос в том же шаблоне, вы можете использовать {{ _self.menu_links(links) }}.
грипп
спасибо, от мысли об этом у меня заболел мозг, но твой ответ имеет смысл.
azzy81
У меня была одна проблема с моим проектом с комментариями. подкомментарии (подссылки) также были включены в основную коллекцию (ссылки). поэтому перед включением мне пришлось проверить, есть ли у комментария «родительская» запись.
Евгений Смирнов
4
Использование {{_self.menu_links}}- плохая практика ! Прочтите примечание здесь: макрос Когда вы определяете макрос в шаблоне, в котором собираетесь его использовать, у вас может возникнуть соблазн вызвать макрос напрямую через _self.input () вместо его импорта; даже если кажется, что это работает, это всего лишь побочный эффект текущей реализации, и он больше не будет работать в Twig 2.x. Вам следует снова импортировать макросы локально, инсайтmenu_links
dr.scre
37

Веточка 2.0 - 2.11

Если вы хотите использовать макрос в том же шаблоне , вы должны использовать что-то вроде этого, чтобы оставаться совместимым с 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>

Это расширяет random-coderответ и включает dr.screподсказку к документации Twig о макросах, которые теперь можно использовать._self , но импортировать локально.

Веточка> = 2,11

Начиная с Twig 2.11 , вы можете опустить {% import _self as macros %}, так как встроенные макросы автоматически импортируются в _selfпространство имен (см. Объявление Twig: Автоматический импорт макросов ):

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

<ul class="main-menu">
    {{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>
грипп
источник
2

Если вы используете PHP 5.4 или выше, есть замечательное новое решение (по состоянию на май 2016 г.) этой проблемы от Алена Тьембло: https://github.com/ninsuo/jordan-tree .

Это тег «дерево», который служит именно этой цели. Разметка будет выглядеть так:

{% 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 %}
Иордания Лев
источник
1
Вы не можете передавать дополнительные переменные subtree. В моем случае код должен знать, будет ли больше потомков, и он передает количество уровней макросу, чтобы он мог выполнить <div class="{{ classes[current_level].wrapper }} {% if levels > current_level %}accordion-wrapper{% endif %}">. Для вычисления этого уровня потребуется повторять текущий уровень во второй раз, просто чтобы определить, есть ли дочерние элементы.
chx
1

Сначала я подумал, что это можно решить простым способом, но это не так просто.

Вам нужно создать логику, возможно, с помощью метода класса PHP, когда нужно включать подшаблон Twig, а когда нет.

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

Таким образом, вы можете использовать специальную переменную цикла Twig , которая доступна внутри цикла Twig for . Но я не уверен в объеме этой переменной цикла .

Эта и другая информация доступна на Twigs "для" Docu !

domi27
источник
1

Взял ответ от гриппа и немного изменил его:

{# 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) }}
Сергей Атрощенко
источник
-1

Ответы здесь приводят меня к моему решению.

У меня есть сущность категории с ассоциацией "многие к одному" (родитель для детей).

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

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

В моем шаблоне Twig я визуализирую древовидное представление следующим образом:

<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>
Патрик Роберт Гутерсон
источник
Что делать, если у вас более одного уровня иерархии категорий?
pmoubed