How to render a tree in Twig

2019-01-08 05:29发布

I would like to render a tree with an undetermined depth (children of children of children, etc.). I need to loop through the array recursively; how can I do this in Twig?

6条回答
啃猪蹄的小仙女
2楼-- · 2019-01-08 05:46

The answers here lead my to my Solution.

I have a Category Entity with self referencing ManyToOne association (parent to children).

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

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

In my twig template i am rendering the tree view like this:

<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>
查看更多
放我归山
3楼-- · 2019-01-08 05:50

Took flu's answer and modified it a little:

{# 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) }}
查看更多
再贱就再见
4楼-- · 2019-01-08 05:55

If you're running PHP 5.4 or higher, there is a wonderful new solution (as of May 2016) to this problem by Alain Tiemblo: https://github.com/ninsuo/jordan-tree.

It's a "tree" tag that serves this exact purpose. Markup would look like this:

{% 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 %}
查看更多
Root(大扎)
5楼-- · 2019-01-08 06:00

First i thought, this may be solved straightforward - but it isn't that easy.

You need to create a logic, maybe with a php class method, when to include a twig subtemplate and when not.

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

So you could use the special twig loop variable , which is available inside a twig for loop. But i'm not sure about the scope of this loop variable.

Sorry for provide only an approach not a solution, but perhaps i hope my thoughts may help you (a little bit).

This and other informations are available on Twigs "for" Docu !

查看更多
家丑人穷心不美
6楼-- · 2019-01-08 06:01

If you want to use a Macro in the same template you should use something like this to stay compatible with 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>

This extends random-coder's answer and incorporates dr.scre's hint to the twig documentation about macros to now use _self but import locally.

查看更多
Animai°情兽
7楼-- · 2019-01-08 06:09

Thanks domi27, I played around with your idea and came up with this. I made a nested array as my tree, ['link']['sublinks'] is null or another array of more of the same.

Templates

The sub-template file to recurse with:

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

Then in the main template call this (kinda redundant 'with' stuff there):

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

Macros

A similar effect can be achieved with 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 %}

In the main template do this:

{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>

Hope it helps :)

查看更多
登录 后发表回答