extends

La etiqueta extends se puede utilizar para extender una plantilla desde otra.

Note

Al igual que PHP, Twig no admite herencia múltiple. Por lo tanto, solo puedes tener una etiqueta extends por renderizado. Sin embargo, Twig admite reutilización horizontal.

Definamos una plantilla base, base.html.twig, que define un documento HTML esqueleto simple:

<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <link rel="stylesheet" href="style.css"/>
            <title>{% block title %}{% endblock %} - Mi Página Web</title>
        {% endblock %}
    </head>
    <body>
        <div id="content">{% block content %}{% endblock %}</div>
        <div id="footer">
            {% block footer %}
                &copy; Copyright 2011 por <a href="https://example.com/">tú</a>.
            {% endblock %}
        </div>
    </body>
</html>

En este ejemplo, las etiquetas block definen cuatro bloques que las plantillas hijas pueden rellenar.

Todo lo que hace la etiqueta block es indicar al motor de plantillas que una plantilla hija puede sobrescribir esas porciones de la plantilla.

Plantilla Hija

Una plantilla hija podría verse así:

{% extends "base.html.twig" %}

{% block title %}Índice{% endblock %}
{% block head %}
    {{ parent() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Índice</h1>
    <p class="important">
        Bienvenido a mi increíble página de inicio.
    </p>
{% endblock %}

La etiqueta extends es la clave aquí. Le dice al motor de plantillas que esta plantilla "extiende" otra plantilla. Cuando el sistema de plantillas evalúa esta plantilla, primero localiza al padre. La etiqueta extends debe ser la primera etiqueta en la plantilla.

Ten en cuenta que, dado que la plantilla hija no define el bloque footer, se utiliza el valor de la plantilla padre en su lugar.

No puedes definir múltiples etiquetas block con el mismo nombre en la misma plantilla. Esta limitación existe porque una etiqueta block funciona en "ambas" direcciones. Es decir, una etiqueta block no solo proporciona un agujero para llenar, sino que también define el contenido que llena el agujero en el padre. Si hubiera dos etiquetas block con nombres similares en una plantilla, el padre de esa plantilla no sabría cuál de los contenidos de los bloques usar.

Si deseas imprimir un bloque varias veces, sin embargo, puedes usar la función block:

<title>{% block title %}{% endblock %}</title>
<h1>{{ block('title') }}</h1>
{% block body %}{% endblock %}

Bloques Padre

Es posible renderizar los contenidos del bloque padre usando la función parent. Esto devuelve los resultados del bloque padre:

{% block sidebar %}
    <h3>Tabla de Contenidos</h3>
    ...
    {{ parent() }}
{% endblock %}

Etiquetas de Fin de Bloque Nombradas

Twig te permite poner el nombre del bloque después de la etiqueta de fin para una mejor legibilidad (el nombre después de la palabra endblock debe coincidir con el nombre del bloque):

{% block sidebar %}
    {% block inner_sidebar %}
        ...
    {% endblock inner_sidebar %}
{% endblock sidebar %}

Anidamiento y Ámbito de Bloques

Los bloques pueden anidarse para diseños más complejos. Por defecto, los bloques tienen acceso a variables de ámbitos externos:

{% for item in seq %}
    <li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}

Atajos de Bloques

Para bloques con poco contenido, es posible usar una sintaxis abreviada. Las siguientes construcciones hacen lo mismo:

{% block title %}
    {{ page_title|title }}
{% endblock %}
{% block title page_title|title %}

Herencia Dinámica

Twig admite herencia dinámica usando una variable como plantilla base:

{% extends some_var %}

Si la variable se evalúa como una instancia de \Twig\Template o \Twig\TemplateWrapper, Twig la usará como plantilla padre:

// {% extends layout %}

$layout = $twig->load('some_layout_template.html.twig');

$twig->display('template.html.twig', ['layout' => $layout]);

También puedes proporcionar una lista de plantillas que se verifican por existencia. La primera plantilla que exista se usará como padre:

{% extends ['layout.html.twig', 'base_layout.html.twig'] %}

Herencia Condicional

Como el nombre de la plantilla para el padre puede ser cualquier expresión válida de Twig, es posible hacer que el mecanismo de herencia sea condicional:

{% extends standalone ? "minimum.html.twig" : "base.html.twig" %}

En este ejemplo, la plantilla extenderá la plantilla de diseño "minimum.html.twig" si la variable standalone se evalúa como true, y "base.html.twig" en caso contrario.

¿Cómo funcionan los bloques?

Un bloque proporciona una forma de cambiar cómo se renderiza una determinada parte de una plantilla, pero no interfiere de ninguna manera con la lógica que lo rodea.

Tomemos el siguiente ejemplo para ilustrar cómo funciona un bloque y, lo que es más importante, cómo no funciona:

{# base.html.twig #}
{% for post in posts %}
    {% block post %}
        <h1>{{ post.title }}</h1>
        <p>{{ post.body }}</p>
    {% endblock %}
{% endfor %}

Si renderizas esta plantilla, el resultado sería exactamente el mismo con o sin la etiqueta block. El block dentro del bucle for es solo una forma de hacerlo sobrescribible por una plantilla hija:

{# child.html.twig #}
{% extends "base.html.twig" %}

{% block post %}
    <article>
        <header>{{ post.title }}</header>
        <section>{{ post.text }}</section>
    </article>
{% endblock %}

Ahora, al renderizar la plantilla hija, el bucle usará el bloque definido en la plantilla hija en lugar del definido en la base; la plantilla ejecutada es entonces equivalente a la siguiente:

{% for post in posts %}
    <article>
        <header>{{ post.title }}</header>
        <section>{{ post.text }}</section>
    </article>
{% endfor %}

Tomemos otro ejemplo: un bloque incluido dentro de una sentencia if:

{% if posts is empty %}
    {% block head %}
        {{ parent() }}

        <meta name="robots" content="noindex, follow">
    {% endblock head %}
{% endif %}

Contrario a lo que podrías pensar, esta plantilla no define un bloque condicionalmente; solo hace sobrescribible por una plantilla hija la salida de lo que se renderizará cuando la condición sea true.

Si quieres que la salida se muestre condicionalmente, usa lo siguiente en su lugar:

{% block head %}
    {{ parent() }}

    {% if posts is empty %}
        <meta name="robots" content="noindex, follow">
    {% endif %}
{% endblock head %}

Note

Ver también

block, block, parent, use