Twig para diseñadores de plantillas

Este documento describe la sintaxis y semántica del motor de plantillas y será muy útil como referencia para quién esté creando plantillas Twig.

Sinopsis

Una plantilla simplemente es un archivo de texto. Esta puede generar cualquier formato basado en texto (HTML, XML, CSV, LaTeX, etc.) No tiene una extensión específica, .html o .xml están muy bien.

Una plantilla contiene variables o expresiones, las cuales se reemplazan por valores cuando se evalúa la plantilla, y las etiquetas, controlan la lógica de la plantilla.

A continuación mostramos una plantilla mínima que ilustra algunos conceptos básicos. Veremos los detalles más adelante en este documento:

<!DOCTYPE html>
<html>
    <head>
        <title>My Webpage</title>
    </head>
    <body>
        <ul id="navigation">
        {% for item in navigation %}
            <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
        {% endfor %}
        </ul>

        <h1>My Webpage</h1>
        {{ a_variable }}
    </body>
</html>

Hay dos tipos de delimitadores: {% ... %} y {{ ... }}. El primero se utiliza para ejecutar declaraciones como bucles for, el último imprime en la plantilla el resultado de una expresión.

Integrando con IDEs

Los IDEs modernos son compatibles con el resaltado de sintaxis y autocompletado en una amplia gama de lenguajes.

Variables

La aplicación pasa variables a las plantillas para que puedas combinarlas en la plantilla. Las variables pueden tener atributos o elementos en ellas a los cuales puedes acceder también. Cómo se ve una variable, en gran medida, depende de la aplicación que la proporcione.

Puedes utilizar un punto (.) para acceder a los atributos de una variable (métodos o propiedades de un objeto PHP, o elementos de un arreglo PHP), o la así llamada sintaxis de «subíndice» ([]).

{{ foo.bar }}
{{ foo['bar'] }}

Cuando el atributo contiene caracteres especiales (como el guión - se debe interpretar como el operador de sustracción), en su lugar usa la función attribute() para acceder al valor de la variable attribute:

{# equivalente al no funcional foo.data-foo #}
{{ attribute(foo, 'data-foo') }}

Nota

Es importante saber que las llaves no son parte de la variable, sino de la declaración de impresión. Si accedes a variables dentro de las etiquetas no las envuelvas con llaves.

Si no existe una variable o atributo, recibirás un valor nulo cuando la opción strict_variables está ajustada a false, de lo contrario Twig lanzará un error (consulta las opciones de entorno).

Nota

Si deseas obtener un atributo dinámico en una variable, utiliza la función attribute en su lugar.

Variables globales

Las siguientes variables siempre están disponibles en las plantillas:

  • _self: hace referencia a la plantilla actual;
  • _context: hace referencia al contexto actual;
  • _charset: hace referencia al juego de caracteres actual.

Definiendo variables

Puedes asignar valores a las variables dentro de los bloques de código. Las asignaciones usan la etiqueta set:

{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}

Filtros

Los filtros pueden modificar variables. Los filtros están separados de la variable por un símbolo de tubo (|) y pueden tener argumentos opcionales entre paréntesis. Puedes encadenar múltiples filtros. La salida de un filtro se aplica al siguiente.

El siguiente ejemplo elimina todas las etiquetas HTML del name y lo formatea como nombre propio:

{{ name|striptags|title }}

Los filtros que aceptan argumentos llevan paréntesis en torno a los argumentos. Este ejemplo unirá una lista con comas:

{{ list|join(', ') }}

Para aplicar un filtro en una sección de código, envuélvelo con la etiqueta filter:

{% filter upper %}
  Este texto cambia a mayúsculas
{% endfilter %}

Ve a la página de filtros para aprender más acerca de los filtros incorporados.

Funciones

Las funciones se pueden llamar para generar contenido. Las funciones son llamadas por su nombre seguido de paréntesis (()) y pueden tener argumentos.

Por ejemplo, la función range devuelve una lista que contiene una progresión aritmética de números enteros:

{% for i in range(0, 3) %}
    {{ i }},
{% endfor %}

Ve a la página funciones para aprender más acerca de las funciones incorporadas.

Argumentos nombrados

Nuevo en la versión 1.12: El soporte para los argumentos nombrados se añadió en Twig 1.12.

Los argumentos para filtros y funciones también se pueden pasar como argumentos nombrados:

{% for i in range(low=1, high=10, step=2) %}
    {{ i }},
{% endfor %}

Utilizar argumentos nombrados hace tus plantillas más explícitas sobre el significado de los valores que pasas como argumentos:

{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}

{# contra #}

{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}

Los argumentos nombrados también te permiten omitir algunos argumentos para los que no quieras cambiar el valor predefinido:

{# el primer argumento es el formato de fecha, el cual por omisión es el formato de fecha global si pasas null #}
{{ "now"|date(null, "Europe/Paris") }}

{# u omites el valor de formato utilizando un argumento nombrado para timezone #}
{{ "now"|date(timezone="Europe/Paris") }}

También puedes utilizar ambos argumentos posicionales y nombrados en una llamada, lo cual no se recomienda cuando pueda provocar alguna confusión:

{# ambos trabajan #}
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
{{ "now"|date(timezone="Europe/Paris", 'd/m/Y H:i') }}

Truco

Cada función y página de documentación del filtro tiene una sección donde los nombres de todos los argumentos están listados cuándo cuentan con apoyo.

Estructuras de control

Una estructura de control se refiere a todas esas cosas que controlan el flujo de un programa — condicionales (es decir, if/elseif/else), bucles for, así como cosas tales como bloques. Las estructuras de control aparecen dentro de bloques {% ... %}.

Por ejemplo, para mostrar una lista de usuarios provista en una variable llamada users, usa la etiqueta for:

<h1>Members</h1>
<ul>
    {% for user in users %}
        <li>{{ user.username|e }}</li>
    {% endfor %}
</ul>

Puedes utilizar la etiqueta if para probar una expresión:

{% if users|length > 0 %}
    <ul>
        {% for user in users %}
            <li>{{ user.username|e }}</li>
        {% endfor %}
    </ul>
{% endif %}

Ve a la página etiquetas para aprender más acerca de las etiquetas incorporadas.

Comentarios

Para comentar parte de una línea en una plantilla, utiliza la sintaxis de comentario {# ... #}. Esta es útil para depuración o para agregar información para los diseñadores de otra plantilla o para ti mismo:

{# nota: inhabilitado en la plantilla porque ya no se utiliza
    {% for user in users %}
        ...
    {% endfor %}
#}

Incluyendo otras plantillas

La etiqueta include es útil para incluir una plantilla y devolver el contenido producido de esa plantilla a la actual:

{% include 'sidebar.html' %}

De manera predeterminada se pasa el contexto actual a las plantillas incluidas.

El contexto que se pasa a la plantilla incluida incorpora las variables definidas en la plantilla:

{% for box in boxes %}
    {% include "render_box.html" %}
{% endfor %}

La plantilla incluida render_box.html es capaz de acceder a box.

El nombre de archivo de la plantilla depende del gestor de plantillas. Por ejemplo, el Twig_Loader_Filesystem te permite acceder a otras plantillas, dando el nombre del archivo. Puedes acceder a plantillas en subdirectorios con una barra inclinada:

{% include "sections/articles/sidebar.html" %}

Este comportamiento depende de la aplicación en que integres Twig.

Herencia en plantillas

La parte más poderosa de Twig es la herencia entre plantillas. La herencia de plantillas te permite crear un «esqueleto» de plantilla base que contenga todos los elementos comunes de tu sitio y define los bloques que las plantillas descendientes pueden sustituir.

Suena complicado pero es muy básico. Es más fácil entenderlo comenzando con un ejemplo.

Vamos a definir una plantilla base, base.html, la cual define el esqueleto de un documento HTML simple que puedes usar para una sencilla página de dos columnas:

<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <link rel="stylesheet" href="style.css" />
            <title>{% block title %}{% endblock %} - My Webpage</title>
        {% endblock %}
    </head>
    <body>
        <div id="content">{% block content %}{% endblock %}</div>
        <div id="footer">
            {% block footer %}
                &copy; Copyright 2011 by <a href="http://dominio.invalido/"></a>.
            {% endblock %}
        </div>
    </body>
</html>

En este ejemplo, las etiquetas block definen cuatro bloques que las plantillas herederas pueden rellenar. Todas las etiquetas block le dicen al motor de plantillas que una plantilla heredera puede sustituir esas porciones de la plantilla.

Una plantilla hija podría tener este aspecto:

{% extends "base.html" %}

{% block title %}Index{% endblock %}
{% block head %}
    {{ parent() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
        Bienvenido a mi impresionante página.
    </p>
{% endblock %}

Aquí, la clave es la etiqueta extends. Esta le dice al motor de plantillas que esta plantilla «extiende» otra plantilla. Cuando el sistema de plantillas evalúa esta plantilla, en primer lugar busca la plantilla padre. La etiqueta extends debe ser la primera etiqueta en la plantilla.

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

Es posible reproducir el contenido del bloque padre usando la función parent. Esta devuelve el resultado del bloque padre:

{% block sidebar %}
    <h3>Table Of Contents</h3>
    ...
    {{ parent() }}
{% endblock %}

Truco

La página de documentación para la etiqueta extends describe características más avanzadas como el anidamiento de bloques, ámbito, herencia dinámica, y herencia condicional.

Nota

Twig también es compatible con herencia múltiple por medio del así llamado reuso horizontal con la ayuda de la etiqueta use. Esta es una característica que casi nunca se necesita en las plantillas normales.

Escapando HTML

Cuando generas HTML desde plantillas, siempre existe el riesgo de que una variable incluya caracteres que afecten el HTML resultante. Hay dos enfoques: escapar cada variable manualmente o de manera predeterminada escapar todo automáticamente.

Twig apoya ambos, el escape automático está habilitado por omisión.

Nota

El escape automático sólo se admite si has habilitado la extensión escaper (el cual es el valor predeterminado).

Trabajando con el escape manual

Si el escape manual está habilitado, es tu responsabilidad escapar las variables si es necesario. ¿Qué escapar? Cualquier variable en que no confíes.

El escape trabaja entubando la variable a través del filtro escape o |e:

{{ user.username|e }}

De manera predeterminada, el filtro escape utiliza la estrategia html, pero dependiendo del contexto a escapar, posiblemente desees usar otras estrategias disponibles explícitamente:

{{ user.username|e(‘js’) }} {{ user.username|e(‘css’) }} {{ user.username|e(‘url’) }} {{ user.username|e(‘html_attr’) }}

Trabajando con escape automático

Ya sea que el escape automático esté habilitado o no, puedes marcar una sección de una plantilla para que sea escapada o no utilizando la etiqueta autoescape:

{% autoescape %}
    En este bloque, todo se escapará automáticamente (usando
    la estrategia HTML)
{% endautoescape %}

De manera predeterminada, el autoescape utiliza la estrategia de escape HTML. Si sacas variables en otros contextos, es necesario escaparlas explícitamente con la estrategia de escape adecuada:

{% autoescape 'js' %}
    En este bloque, todo se escapará automáticamente (usando
    la estrategia JS)
{% endautoescape %}

Escapando

A veces es deseable e incluso necesario contar con que Twig omita partes que de lo contrario manejaría como variables o bloques. Por ejemplo, si utilizas la sintaxis predeterminada y deseas utilizar {{ como cadena sin procesar en la plantilla y no iniciar una variable, tienes que usar un truco.

La forma más sencilla es extraer la variable del delimitador ({{) usando una expresión variable:

{{ '{{' }}

Para grandes secciones tiene sentido marcar un bloque como verbatim.

Macros

Nuevo en la versión 1.12: El soporte para valores predefinidos de argumentos se añadió en Twig 1.12.

Las macros son comparables con funciones en lenguajes de programación regulares. Son útiles para reutilizar HTML usado frecuentemente para no repetirlos tú mismo.

Una macro se define a través de la etiqueta macro. Aquí está un pequeño ejemplo (más tarde llamada forms.html) de una macro que pinta un elemento de formulario:

{% macro input(name, value, type, size) %}
    <input type="{{ type|default('text') }}"
             name="{{ name }}"
             value="{{ value|e }}"
             size="{{ size|default(20) }}" />
{% endmacro %}

Puedes definir macros en cualquier plantilla, y necesitas «importarlas» con la etiqueta import antes de poder usarlas:

{% import "formularios.html" as forms %}

<p>{{ forms.input('username') }}</p>

Alternativamente, puedes importar nombres de macros individuales desde una plantilla al espacio de nombres actual vía la etiqueta from:

{% importa el input de 'forms.html' como input_field %}

<dl>
    <dt>Username</dt>
    <dd>{{ input_field('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ input_field('password', '', 'password') }}</dd>
</dl>

También puedes definir valores predefinidos para argumentos de macros cuando no se proporcionen en una llamada a la macro:

{% macro input(name, value = "", type = "text", size = 20) %}
    <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}

Expresiones

Twig acepta expresiones en cualquier parte. Estas funcionan de manera muy similar a PHP regular e incluso si no estás trabajando con PHP te debes sentir cómodo con estas.

Nota

La precedencia de los operadores es la siguiente, mostrando los operadores de menor precedencia en primer lugar: b-and, b-xor, b-or, or, and, ==, !=, <, >, >=, <=, in, .., +, -, ~, *, /, //, %, is, and **.

Literales

Nuevo en la versión 1.5: El soporte para codificar claves como nombres y expresiones se añadió en Twig 1.5.

La forma más simple de las expresiones son literales. Los literales son representaciones para tipos PHP, tal como cadenas, números y arreglos. Existen los siguientes literales:

  • "Hello World": Todo lo que esté entre comillas simples o dobles es una cadena. Son útiles cuando necesitas una cadena en la plantilla (por ejemplo, como argumentos para llamadas a función, filtros o simplemente para extender o incluir una plantilla).

  • 42 / 42.23: Números enteros y números en coma flotante se crean tan sólo escribiendo el número. Si está presente un punto es un número en coma flotante, de lo contrario es un número entero.

  • ["foo", "bar"]: Los arreglos se definen por medio de una secuencia de expresiones separadas por una coma (,) y envueltas entre corchetes ([]).

  • {"foo": "bar"}: Los valores hash se definen con una lista de claves y valores separados por una coma (,) y envueltos entre llaves ({}).

    {# claves como cadena #}
    { 'foo': 'foo', 'bar': 'bar' }
    
    {# claves como nombres (equivalente al hash anterior) -- a partir
       de Twig 1.5 #}
    { foo: 'foo', bar: 'bar' }
    
    {# claves como entero #}
    { 2: 'foo', 4: 'bar' }
    
    {# claves como expresiones (la expresión se debe encerrar entre
       paréntesis) -- a partir de Twig 1.5 #}
    { (1 + 1): 'foo', (a ~ 'b'): 'bar' }
    
  • true / false: true representa el valor verdadero, false representa el valor falso.

  • null: null no representa un valor específico. Este es el valor devuelto cuando una variable no existe. none es un alias para null.

Los arreglos y hashes se pueden anidar:

{% set foo = [1, {"foo": "bar"}] %}

Truco

Utilizar comillas dobles o sencillas en cadenas no tiene ningún impacto en el rendimiento pero la interpolación de cadenas sólo cuenta con apoyo en cadenas entre comillas dobles.

Matemáticas

Twig te permite calcular valores. Esto no suele ser útil en las plantillas, pero existe por el bien de la integridad. Admite los siguientes operadores:

  • +: Suma dos objetos (los operandos se convierten a números). {{ 1 + 1 }} is 2.
  • -: Resta el segundo número del primero. {{ 3 - 2 }} es 1.
  • /: Divide dos números. El valor devuelto será un número en punto flotante. {{ 1 / 2 }} es {{ 0.5 }}.
  • %: Calcula el residuo de una división entera. {{ 11 % 7 }} es 4.
  • //: Divide dos números y devuelve el resultado entero truncado. {{ 20 // 7 }} es 2.
  • *: Multiplica el operando de la izquierda con el de la derecha. {{ 2 * 2 }} devolverá 4.
  • **: Eleva el operando izquierdo a la potencia del operando derecho. {{ 2 ** 3 }} devolverá 8.

Lógica

Puedes combinar varias expresiones con los siguientes operadores:

  • and: Devuelve true si ambos operandos izquierdo y derecho son true.
  • or: Devuelve true si el operando izquierdo o derecho es true.
  • not: Niega una declaración.
  • (expr): Agrupa una expresión.

Nota

Twig además es compatible con operadores a nivel de bits (b-and, b-xor, and b-or).

Comparaciones

Los siguientes operadores de comparación son compatibles con cualquier expresión: ==, !=, <, >, >=, y <=.

Operador de contención

El operador in realiza la prueba de contención.

Esta devuelve true si el operando de la izquierda figura entre los de la derecha:

{# devuelve true #}

{{ 1 in [1, 2, 3] }}

{{ 'cd' in 'abcde' }}

Truco

Puedes utilizar este filtro para realizar una prueba de contención en cadenas, arreglos u objetos que implementan la interfaz Traversable.

Para llevar a cabo una prueba negativa, utiliza el operador not in:

{% if 1 not in [1, 2, 3] %}

{# es equivalente a #}
{% if not (1 in [1, 2, 3]) %}

Operador de prueba

El operador is realiza pruebas. Puedes utilizar las pruebas para comprobar una variable con una expresión común. El operando de la derecha es el nombre de la prueba:

{# averigua si una variable es impar #}

{{ nombre is odd }}

Las pruebas también pueden aceptar argumentos:

{% if loop.index is divisibleby(3) %}

Puedes negar las pruebas usando el operador is not:

{% if loop.index is not divisibleby(3) %}

{# es equivalente a #}
{% if not (loop.index is divisibleby(3)) %}

Ve a la página Probando para aprender más sobre las pruebas integradas.

Otros operadores

Nuevo en la versión 1.12.0: El soporte para el operador ternario extendido se añadió en Twig 1.12.0.

Los siguientes operadores son muy útiles pero no encajan en ninguna de las otras dos categorías:

  • ..: Crea una secuencia basada en el operando antes y después del operador (esta sólo es pura azúcar sintáctica para la función range).

  • |: Aplica un filtro.

  • ~: Convierte todos los operandos en cadenas y los concatena. {{ "Hello " ~ name ~ "!" }} debería devolver (suponiendo que name es 'John') Hello John!.

  • ., []: Obtiene un atributo de un objeto.

  • ?:: El operador ternario:

    {{ foo ? 'yes' : 'no' }}
    
    {# a partir de Twig 1.12.0 #}
    {{ foo ?: 'no' }} == {{ foo ? foo : 'no' }}
    {{ foo ? 'yes' }} == {{ foo ? 'yes' : '' }}

Interpolando cadenas

Nuevo en la versión 1.5: La interpolación de cadenas se añadió en Twig 1.5.

La interpolación de cadenas (#{expresión}) permite que cualquier expresión válida aparezca dentro de una cadena entre comillas dobles. El resultado de la evaluación esa expresión se inserta en la cadena:

{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}

Controlando el espacio en blanco

Nuevo en la versión 1.1: La etiqueta para controlar el nivel de los espacios en blanco se añadió en Twig 1.1.

La primer nueva línea después de una etiqueta de plantilla se elimina automáticamente (como en PHP). El motor de plantillas no modifica el espacio en blanco, por lo tanto cada espacio en blanco (espacios, tabuladores, nuevas líneas, etc.) se devuelve sin cambios.

Utiliza la etiqueta spaceless para quitar los espacios en blanco entre las etiquetas HTML:

{% spaceless %}
    <div>
        <strong>foo</strong>
    </div>
{% endspaceless %}

{# Producirá <div><strong>foo</strong></div> #}

Además de la etiqueta spaceless también puedes controlar los espacios en blanco a nivel de etiquetas. Utilizando el modificador de control de los espacios en blanco en tus etiquetas, puedes recortar los espacios en blanco en ambos extremos:

{% set value = 'no spaces' %}
{#- No deja espacios en blanco en ambos extremos -#}
{%- if true -%}
    {{- value -}}
{%- endif -%}

{# produce 'sin espacios' #}

El ejemplo anterior muestra el modificador de control de espacios en blanco predeterminado, y cómo lo puedes utilizar para quitar los espacios en blanco alrededor de las etiquetas. Recortar el espacio debe consumir todos los espacios en blanco a ese lado de la etiqueta. Es posible utilizar el recorte de espacios en blanco en un lado de una etiqueta:

{% set value = 'no spaces' %}
<li>    {{- value }}    </li>

{# produce '<li>no spaces    </li>' #}

Extendiendo

Puedes extender Twig fácilmente.

Si estás buscando nuevas etiquetas, filtros, o funciones, échale un vistazo al repositorio de extensiones oficial de Twig.

Si deseas crear una propia, lee el capítulo Creando una extensión.

Bifúrcame en GitHub