Plantillas

Nette utiliza el sistema de plantillas Latte. Por un lado, porque es el sistema de plantillas más seguro para PHP y, al mismo tiempo, el sistema más intuitivo. No necesita aprender mucho nuevo, le basta con conocer PHP y algunas etiquetas.

Es habitual que una página se componga de una plantilla de layout + la plantilla de la acción dada. Así puede verse, por ejemplo, una plantilla de layout, observe los bloques {block} y la etiqueta {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}Mi Aplicación{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

Y esta será la plantilla de la acción:

{block title}Página de inicio{/block}

{block content}
<h1>Página de inicio</h1>
...
{/block}

Esta define el bloque content, que se inserta en lugar de {include content} en el layout, y también re-define el bloque title, que sobrescribe {block title} en el layout. Intente imaginar el resultado.

Búsqueda de plantillas

No necesita indicar en los presenters qué plantilla se debe renderizar, el framework deduce la ruta por sí mismo y le ahorra escribir.

Si utiliza una estructura de directorios donde cada presenter tiene su propio directorio, simplemente coloque la plantilla en este directorio bajo el nombre de la acción (resp. vista), es decir, para la acción default use la plantilla default.latte:

app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Si utiliza una estructura donde los presenters están juntos en un directorio y las plantillas en la carpeta templates, guárdela ya sea en el archivo <Presenter>.<view>.latte o <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1ª variante
        └── Home/
            └── default.latte   ← 2ª variante

El directorio templates también puede estar ubicado un nivel más arriba, es decir, al mismo nivel que el directorio con las clases de los presenters.

Si no se encuentra la plantilla, el presenter responde con un error 404 – página no encontrada.

Puede cambiar la vista usando $this->setView('otraVista'). También puede especificar directamente el archivo de plantilla usando $this->template->setFile('/ruta/a/plantilla.latte').

Los archivos donde se buscan las plantillas se pueden cambiar sobrescribiendo el método formatTemplateFiles(), que devuelve un array de posibles nombres de archivo.

Búsqueda de la plantilla de layout

Nette también localiza automáticamente el archivo de layout.

Si utiliza una estructura de directorios donde cada presenter tiene su propio directorio, coloque el layout ya sea en la carpeta con el presenter, si es específico solo para él, o un nivel más arriba, si es común para varios presenters:

app/
└── Presentation/
    ├── @layout.latte           ← layout común
    └── Home/
        ├── @layout.latte       ← solo para el presenter Home
        ├── HomePresenter.php
        └── default.latte

Si utiliza una estructura donde los presenters están juntos en un directorio y las plantillas en la carpeta templates, se esperará el layout en estos lugares:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← layout común
        ├── Home.@layout.latte  ← solo para Home, 1ª variante
        └── Home/
            └── @layout.latte   ← solo para Home, 2ª variante

Si el presenter se encuentra en un módulo, también se buscará en niveles de directorio superiores, según el anidamiento del módulo.

El nombre del layout se puede cambiar usando $this->setLayout('layoutAdmin') y entonces se esperará en el archivo @layoutAdmin.latte. También se puede especificar directamente el archivo de plantilla de layout usando $this->setLayout('/ruta/a/plantilla.latte').

Usando $this->setLayout(false) o la etiqueta {layout none} dentro de la plantilla, se desactiva la búsqueda de layout.

Los archivos donde se buscan las plantillas de layout se pueden cambiar sobrescribiendo el método formatLayoutTemplateFiles(), que devuelve un array de posibles nombres de archivo.

Variables en la plantilla

Pasamos variables a la plantilla escribiéndolas en $this->template y luego las tenemos disponibles en la plantilla como variables locales:

$this->template->article = $this->articles->getById($id);

De esta manera simple podemos pasar cualquier variable a las plantillas. Sin embargo, en el desarrollo de aplicaciones robustas, suele ser más útil limitarse. Por ejemplo, definiendo explícitamente la lista de variables que espera la plantilla y sus tipos. Gracias a esto, PHP podrá verificar los tipos, el IDE sugerir correctamente y el análisis estático detectar errores.

¿Y cómo definimos tal lista? Simplemente en forma de una clase y sus propiedades. La nombramos de manera similar al presenter, solo que con Template al final:

/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// y otras variables
}

El objeto $this->template en el presenter será ahora una instancia de la clase ArticleTemplate. Así que PHP verificará los tipos declarados al escribir. Y a partir de la versión PHP 8.2 advertirá también sobre la escritura en una variable inexistente, en versiones anteriores se puede lograr lo mismo usando el trait Nette\SmartObject.

La anotación @property-read está destinada al IDE y al análisis estático, gracias a ella funcionará la sugerencia, ver PhpStorm y autocompletado de código para $this⁠-⁠>⁠template.

Puede disfrutar del lujo de la sugerencia también en las plantillas, basta con instalar el plugin para Latte en PhpStorm e indicar al principio de la plantilla el nombre de la clase, más en el artículo Latte: cómo usar el sistema de tipos:

{templateType App\Presentation\Article\ArticleTemplate}
...

Así funcionan también las plantillas en los componentes, basta con seguir la convención de nombres y para un componente, p. ej., FifteenControl crear una clase de plantilla FifteenTemplate.

Si necesita crear $template como una instancia de otra clase, utilice el método createTemplate():

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Variables predeterminadas

Los presenters y componentes pasan automáticamente varias variables útiles a las plantillas:

  • $basePath es la ruta URL absoluta al directorio raíz (p. ej., /eshop)
  • $baseUrl es la URL absoluta al directorio raíz (p. ej., http://localhost/eshop)
  • $user es el objeto que representa al usuario
  • $presenter es el presenter actual
  • $control es el componente o presenter actual
  • $flashes array de mensajes enviados por la función flashMessage()

Si utiliza su propia clase de plantilla, estas variables se pasarán si crea una propiedad para ellas.

Creación de enlaces

En la plantilla, se crean enlaces a otros presenters y acciones de esta manera:

<a n:href="Product:show">detalle del producto</a>

El atributo n:href es muy útil para las etiquetas HTML <a>. Si queremos mostrar el enlace en otro lugar, por ejemplo en el texto, usamos {link}:

La dirección es: {link Home:default}

Encontrará más información en el capítulo Creación de enlaces URL.

Filtros personalizados, etiquetas, etc.

El sistema de plantillas Latte se puede extender con filtros, funciones, etiquetas, etc. personalizados. Se puede hacer directamente en el método render<View> o beforeRender():

public function beforeRender(): void
{
	// agregar filtro
	$this->template->addFilter('foo', /* ... */);

	// o configuramos directamente el objeto Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte en la versión 3 ofrece una forma más avanzada y es crear una extension para cada proyecto web. Un ejemplo fragmentario de tal clase:

namespace App\Presentation\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}

La registramos usando la configuración:

latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension

Traducción

Si programa una aplicación multilingüe, probablemente necesitará mostrar algunos textos en la plantilla en diferentes idiomas. Nette Framework define para este propósito una interfaz para la traducción Nette\Localization\Translator, que tiene un único método translate(). Este recibe el mensaje $message, que generalmente suele ser una cadena, y cualquier otro parámetro. La tarea es devolver la cadena traducida. En Nette no hay ninguna implementación predeterminada, puede elegir según sus necesidades entre varias soluciones listas que encontrará en Componette. En su documentación aprenderá cómo configurar el traductor.

A las plantillas se les puede establecer un traductor, que pasamos, con el método setTranslator():

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

El traductor alternativamente se puede establecer mediante la configuración:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Luego, el traductor se puede usar, por ejemplo, como filtro |translate, incluyendo parámetros complementarios que se pasan al método translate() (ver foo, bar):

<a href="basket">{='Carrito'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

O como etiqueta de guion bajo:

<a href="basket">{_'Carrito'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

Para traducir una sección de la plantilla, existe una etiqueta par {translate} (desde Latte 2.11, antes se usaba la etiqueta {_}):

<a href="order">{translate}Pedido{/translate}</a>
<a href="order">{translate foo, bar}Pedido{/translate}</a>

El traductor se llama estándarmente en tiempo de ejecución al renderizar la plantilla. Sin embargo, Latte versión 3 puede traducir todos los textos estáticos ya durante la compilación de la plantilla. Esto ahorra rendimiento, porque cada cadena se traduce solo una vez y la traducción resultante se escribe en la forma compilada. En el directorio de caché se crean así múltiples versiones compiladas de la plantilla, una para cada idioma. Para ello basta con indicar el idioma como segundo parámetro:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Por texto estático se entiende, por ejemplo, {_'hello'} o {translate}hello{/translate}. Los textos no estáticos, como por ejemplo {_$foo}, seguirán traduciéndose en tiempo de ejecución.

versión: 4.0