Cómo renderizar un árbol en Twig

89

Me gustaría renderizar un árbol con una profundidad indeterminada (hijos de hijos de hijos, etc.). Necesito recorrer la matriz de forma recursiva; ¿Cómo puedo hacer esto en Twig?

T-RonX
fuente

Respuestas:

117

Jugué con la idea de domi27 y se me ocurrió esto. Hice una matriz anidada como mi árbol, ['enlace'] ['subvínculos'] es nulo u otra matriz de más de lo mismo.

Plantillas

El archivo de sub-plantilla para recurrir con:

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

Luego, en la plantilla principal, llame a esto (tipo de cosas redundantes 'con' allí):

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

Macros

Se puede lograr un efecto similar con 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 %}

En la plantilla principal, haz esto:

{% import "macros/menu-macros.html" as macros %}
<ul class="main-menu">
    {{ macros.menu_links(links) }}
</ul>
codificador-aleatorio-1920
fuente
9
¡Muy bien, gracias! Si desea utilizar la macro en la misma plantilla, puede utilizar {{ _self.menu_links(links) }}.
gripe
gracias, la idea de esto hizo que me doliera el cerebro, pero tu respuesta tiene mucho sentido.
azzy81
Tuve un problema con mi proyecto con comentarios. También se incluyeron subcomments (sublinks) en la colección principal (links). así que antes de incluir tuve que comprobar si el comentario tenía una entrada "principal".
Jevgeni Smirnov
4
¡Usar {{_self.menu_links}}es una mala práctica ! Lea una nota aquí: macro Cuando define una macro en la plantilla donde la va a usar, puede tener la tentación de llamarla directamente a través de _self.input () en lugar de importarla; incluso si parece funcionar, esto es solo un efecto secundario de la implementación actual y ya no funcionará en Twig 2.x. Debería importar macros localmente una vez más menu_links
insite
35

Ramita 2.0 - 2.11

Si desea usar una macro en la misma plantilla , debe usar algo como esto para seguir siendo compatible con 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>

Esto amplía random-coderla respuesta e incorpora dr.screuna pista a la documentación de Twig sobre macros para usar ahora _self, pero importar localmente.

Ramita> = 2,11

A partir de Twig 2.11 , puede omitir {% import _self as macros %}, ya que las macros en línea se importan automáticamente en el _selfespacio de nombres (consulte Anuncio de Twig: Importación automática de macros ):

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

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

Si está ejecutando PHP 5.4 o superior, existe una nueva y maravillosa solución (a partir de mayo de 2016) para este problema de Alain Tiemblo: https://github.com/ninsuo/jordan-tree .

Es una etiqueta de "árbol" que sirve exactamente para este propósito. El marcado se vería así:

{% 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
fuente
1
No puede pasar variables adicionales a subtree. En mi caso, el código necesita saber si habrá más hijos y pasa el número de niveles a la macro para que pueda hacer un <div class="{{ classes[current_level].wrapper }} {% if levels > current_level %}accordion-wrapper{% endif %}">. Calcular esto requeriría iterar el nivel actual por segunda vez solo para capturar si hay hijos.
chx
1

Primero pensé que esto podría resolverse de una manera sencilla, pero no es tan fácil.

Necesita crear lógica, tal vez con un método de clase PHP, cuándo incluir una subplantilla Twig y cuándo no.

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

Por lo tanto, podría usar la variable de bucle especial Twig , que está disponible dentro de un bucle Twig for . Pero no estoy seguro del alcance de esta variable de ciclo .

Esta y otra información están disponibles en Twigs "para" Docu !

domi27
fuente
0

Tomó la respuesta de la gripe y la modificó un poco:

{# 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
fuente
-1

Las respuestas aquí me llevan a mi solución.

Tengo una entidad de categoría con una asociación de varios a uno que hace referencia a sí misma (de padres a hijos).

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

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

En mi plantilla Twig, estoy representando la vista de árbol de esta manera:

<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
fuente
¿Qué sucede si tiene más de un nivel de jerarquía de categorías?
pm dudado el