Symfony 2.3, el libro oficial

7.2. Herencia de plantillas y layout

Normalmente las plantillas de un mismo proyecto comparten muchos elementos comunes, como por ejemplo la cabecera, el pie de página, una barra lateral, etc. Symfony2 resuelve este problema de forma muy sencilla: una plantilla puede decorar el contenido de otra plantilla.

La idea es exactamente la misma que la herencia de clases PHP: la herencia de plantillas te permite crear una plantilla base llamada layout y que contiene todos los elementos comunes del sitio definidos como bloques. Después, las plantillas hija heredan del layout y rellenan o modifican esos bloques.

En primer lugar, crea un archivo con tu diseño base:

{# app/Resources/views/base.html.twig #}
<!DOCTYPE html>
    <html>
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>{% block title %}Test Application{% endblock %}</title>
        </head>
        <body>
        <div id="sidebar">
            {% block sidebar %}
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog">Blog</a></li>
            </ul>
            {% endblock %}
        </div>

        <div id="contenido">
            {% block body %}{% endblock %}
        </div>
    </body>
</html>
<!-- app/Resources/views/base.html.php -->
<!DOCTYPE html>
    <html>
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?php $view['slots']->output('title', 'Test Application') ?></title>
        </head>
        <body>
        <div id="sidebar">
            <?php if ($view['slots']->has('sidebar')): ?>
                <?php $view['slots']->output('sidebar') ?>
            <?php else: ?>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/blog">Blog</a></li>
                </ul>
            <?php endif; ?>
        </div>

        <div id="contenido">
            <?php $view['slots']->output('body') ?>
        </div>
    </body>
</html>

Nota Aunque la explicación sobre la herencia de plantillas utiliza la terminología propia de Twig, la filosofía de funcionamiento es la misma para Twig y PHP.

Esta plantilla define el esqueleto de una página HTML simple de dos columnas. En este ejemplo, se definen tres bloques con la etiqueta {% block %} (title, sidebar y body). Las plantillas hija pueden modificar los contenidos de cada uno de los bloques o dejarlos tal y como están en la plantilla base.

El siguiente ejemplo muestra el aspecto de una plantilla hija:

{# src/Acme/BlogBundle/Resources/views/Blog/index.html.twig #}
{% extends '::base.html.twig' %}

{% block title %}My cool blog posts{% endblock %}

{% block body %}
    {% for entry in blog_entries %}
        <h2>{{ entry.title }}</h2>
        <p>{{ entry.body }}</p>
    {% endfor %}
{% endblock %}
<!-- src/Acme/BlogBundle/Resources/views/Blog/index.html.php -->
<?php $view->extend('::base.html.php') ?>

<?php $view['slots']->set('title', 'My cool blog posts') ?>

<?php $view['slots']->start('body') ?>
    <?php foreach ($blog_entries as $entry): ?>
        <h2><?php echo $entry->getTitle() ?></h2>
        <p><?php echo $entry->getBody() ?></p>
    <?php endforeach; ?>
<?php $view['slots']->stop() ?>

Nota La plantilla padre se identifica mediante una cadena de texto con una sintaxis especial (::base.html.twig). Los cuatro puntos del principio (::) significan que esta plantilla no pertenece a ningún bundle y que por tanto, se encuentra en el directorio app/Resources/views del proyecto.

La clave de la herencia de plantillas es la etiqueta {% extends %}. De esta forma el motor de plantillas sabe que primero tiene que procesar la plantilla base, que define el diseño de la página y crea varios bloques de contenidos. Después se renderiza la plantilla hija, que reemplaza el contenido de los bloques title y body del padre. El resultado de renderizar esta plantilla hija sería el siguiente:

<!DOCTYPE html>
    <html>
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>My cool blog posts</title>
        </head>
        <body>
        <div id="sidebar">
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog">Blog</a></li>
            </ul>
        </div>

        <div id="contenido">
            <h2>My first post</h2>
            <p>The body of the first post.</p>

            <h2>Another post</h2>
            <p>The body of the second post.</p>
        </div>
    </body>
</html>

Como en la plantilla hija no se ha definido un bloque sidebar, se utiliza el contenido definido en la plantilla padre. Esto mismo sucede para cualquier otro bloque que no haya sido definido por la plantilla hija.

Puedes utilizar tantos niveles de herencia como quieras. En la siguiente sección, se explica el modelo más común de herencia a tres niveles y la forma en que se organizan las plantillas dentro de un proyecto Symfony2.

Cuando trabajes con la herencia de plantillas, ten en cuenta los siguientes consejos:

  • Si incluyes la etiqueta {% extends %} en una plantilla, esta debe ser la primera etiqueta de esa plantilla.
  • Cuantas más etiquetas {% block %} tengas en tu plantilla base, mejor. Recuerda que las plantillas hija no tienen la obligación de rellenar todos los bloques de los padres, por lo que puedes definir tantos bloques como quieras y asignar a cada uno un valor por defecto que sea lógico para la mayor parte de las páginas del sitio. Cuantos más bloques defina el layout, más flexible será su diseño.
  • Si copias y pegas algún contenido en varias plantillas, seguramente será mejor que muevas ese contenido a algún bloque del layout. En otros casos lo mejor es colocar ese contenido en alguna otra plantilla e incluirla con la etiqueta include siempre que sea necesario.
  • Si quieres obtener el contenido de algún bloque de la plantilla padre, utiliza la función {{ parent() }}. Esto es muy útil cuando quieres añadir contenidos propios a cualquier otro contenido que el padre pueda haber definido para ese bloque:
{% block sidebar %}
    <h3>Table of Contents</h3>

    {# ... #}

    {{ parent() }}
{% endblock %}