How to render a tree in Twig

前端 未结 6 1954
灰色年华
灰色年华 2020-11-30 19:20

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条回答
  • 2020-11-30 19:31

    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>
    
    0 讨论(0)
  • 2020-11-30 19:31

    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!

    0 讨论(0)
  • 2020-11-30 19:37

    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) }}
    
    0 讨论(0)
  • 2020-11-30 19:49

    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>
    
    0 讨论(0)
  • 2020-11-30 19:52

    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 %}
    
    0 讨论(0)
  • 2020-11-30 19:54

    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>
    
    0 讨论(0)
提交回复
热议问题