如何在 Twig 中渲染一棵树

我想渲染一棵深度不确定的树(孩子的孩子的孩子,等等)。我需要递归地遍历数组; 如何在 Twig 中实现这一点?

34068 次浏览

First I thought this may be solved in a straightforward way, but it isn't that easy.

You need to create 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.

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

I played around with domi27's 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 (kind of 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>

Twig 2.0 - 2.11

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.

Twig >= 2.11

As of Twig 2.11, you can omit the {% import _self as macros %}, as inlined macros are imported automatically under the _self namespace (see Twig announcement: Automatic macro import):

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


<ul class="main-menu">
\{\{ _self.menu_links(links) }} {# Use _self for inlined macros #}
</ul>

The answers here lead my to my solution.

I have a category entity with a self-referencing many-to-one 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>

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

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) }}