Plantillas
Nette usa el sistema de plantillas Latte. Se usa Latte porque es el sistema de plantillas más seguro para PHP y, a la vez, el más intuitivo. No necesita aprender mucho nuevo; bastará con saber PHP y unas cuantas etiquetas.
Es habitual que una página se componga de una plantilla de layout más la plantilla de la acción concreta. Este podría ser
el aspecto de una plantilla de layout; fíjese en los bloques {block} y en la etiqueta {include}:
<!DOCTYPE html>
<html>
<head>
<title>{block title}My App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
Y esta sería la plantilla de la acción:
{block title}Homepage{/block}
{block content}
<h1>Homepage</h1>
...
{/block}
Define el bloque content, que se inserta en el lugar de {include content} del layout, y redefine
además el bloque title, que sobrescribe el {block title} del layout. Trate de imaginar el
resultado.
Búsqueda de plantillas
En los presenters no necesita indicar qué plantilla debe renderizarse; el framework deduce la ruta automáticamente y le ahorra escribirla.
Si usa una estructura de directorios en la que cada presenter tiene el suyo, basta con colocar la plantilla en ese directorio
con el nombre de la acción (es decir, de la vista). Por ejemplo, para la acción default, use la plantilla
default.latte:
app/
└── Presentation/
└── Home/
├── HomePresenter.php
└── default.latte
Si usa una estructura en la que los presenters están juntos en un directorio y las plantillas en una carpeta
templates, guárdela o bien en el archivo <Presenter>.<vista>.latte, o bien en
<Presenter>/<vista>.latte:
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── Home/
│ └── default.latte ← 1.ª variante
└── Home.default.latte ← 2.ª variante
El directorio templates también se puede colocar 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 con $this->setView('otherView'). También es posible indicar directamente el archivo de
plantilla con $this->template->setFile('/path/to/template.latte').
Los archivos en los que se buscan las plantillas se pueden cambiar sobrescribiendo el método formatTemplateFiles(), que devuelve un array con los posibles nombres de archivo.
Búsqueda de la plantilla de layout
Nette busca también automáticamente el archivo del layout.
Si usa una estructura de directorios en la que cada presenter tiene el suyo, coloque el layout o bien en la carpeta del presenter, si es específico solo de él, o bien un nivel más arriba, si es común a varios presenters:
app/
└── Presentation/
├── @layout.latte ← layout común
└── Home/
├── @layout.latte ← solo para el presenter Home
├── HomePresenter.php
└── default.latte
Si usa una estructura en la que los presenters están agrupados en un directorio y las plantillas en una carpeta
templates, el layout se buscará 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 está en un módulo, la búsqueda continuará además hacia arriba por los niveles de directorio, según el anidamiento de los módulos.
El nombre del layout se puede cambiar con $this->setLayout('layoutAdmin'), y entonces se buscará en el archivo
@layoutAdmin.latte. También puede indicar directamente el archivo de plantilla del layout con
$this->setLayout('/path/to/template.latte').
Usar $this->setLayout(false) o la etiqueta {layout none} dentro de la plantilla desactiva la
búsqueda del layout.
Los archivos en los que se buscan las plantillas de layout se pueden cambiar sobrescribiendo el método formatLayoutTemplateFiles(), que devuelve un array con los posibles nombres de archivo.
Variables de la plantilla
Las variables se pasan a las plantillas escribiéndolas en $this->template. Quedan entonces disponibles en la
plantilla como variables locales:
$this->template->article = $this->articles->getById($id);
Para pasar automáticamente a la plantilla el valor de una propiedad como variable, márquela con el
atributo #[TemplateVariable] y con visibilidad pública:
use Nette\Application\Attributes\TemplateVariable;
class ArticlePresenter extends Nette\Application\UI\Presenter
{
#[TemplateVariable]
public string $siteName = 'My blog';
}
Si pasa a la plantilla una variable con el mismo nombre, #[TemplateVariable] no la sobrescribirá.
Variables predeterminadas
Los presenters y los componentes pasan automáticamente a las plantillas varias variables útiles:
$basePathes la ruta URL absoluta al directorio raíz (por ejemplo,/eshop)$baseUrles la URL absoluta al directorio raíz (por ejemplo,http://localhost/eshop)$useres un objeto que representa al usuario$presenteres el presenter actual$controles el componente o presenter actual$flasheses un array de mensajes enviados con la funciónflashMessage()
Si usa una clase de plantilla propia, estas variables se pasan si crea una propiedad para ellas.
Plantillas con tipos seguros
Al desarrollar aplicaciones robustas resulta útil definir explícitamente qué variables espera la plantilla y de qué tipos son. Esto aporta comprobación de tipos en PHP, sugerencias inteligentes en su IDE y permite que el análisis estático detecte errores.
¿Cómo se define esa lista? Sencillamente, como una clase con propiedades que representan las variables de la plantilla.
Nómbrela como 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 del presenter será ahora una instancia de la clase ArticleTemplate. PHP
comprobará así los tipos declarados al escribir en él.
Nette elige la clase de plantilla automáticamente. Primero busca una clase llamada
<Presenter><Acción>Template, por ejemplo ArticleEditTemplate para la acción
edit, y solo si no existe recurre a <Presenter>Template.
La anotación @property-read es para el IDE y el análisis estático, y habilita el autocompletado; vea PhpStorm y el autocompletado de
$this->template.

También puede usar el autocompletado directamente en las plantillas. Basta con instalar el plugin de Latte para PhpStorm e indicar al principio de la plantilla el nombre de la clase de parámetros; más información en el capítulo Latte: sistema de tipos:
{templateType App\Presentation\Article\ArticleTemplate}
...
Lo mismo vale para los componentes. Basta con seguir la convención de nombres y crear una clase de parámetros
FifteenTemplate para un componente como FifteenControl.
Si necesita usar otra clase de parámetros, use el método createTemplate():
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Si necesita influir en cómo se completa la plantilla antes de renderizarla, por ejemplo para añadir
variables compartidas por todas las acciones, puede sobrescribir el método completeTemplate() del presenter. Se
llama justo antes de renderizar la plantilla:
protected function completeTemplate(Nette\Application\UI\Template $template): void
{
parent::completeTemplate($template);
$template->siteName = 'My blog';
}
Creación de enlaces
En la plantilla, los enlaces a otros presenters y acciones se crean así:
<a n:href="Product:show">product detail</a>
El atributo n:href resulta muy práctico en las etiquetas HTML <a>. Si queremos imprimir el
enlace en otro sitio, por ejemplo dentro de un texto, usamos {link}:
URL is: {link Home:default}
Encontrará más información en el capítulo Creación de enlaces URL.
Filtros, etiquetas y demás propios
El sistema de plantillas Latte se puede ampliar con filtros, funciones, etiquetas y otros elementos propios. Hay tres enfoques disponibles, desde soluciones rápidas ad hoc hasta patrones arquitectónicos para aplicaciones enteras.
Ad hoc en métodos del presenter
El enfoque más rápido es añadir los filtros o funciones directamente en el código del presenter o del componente. En los
presenters van bien para eso los métodos beforeRender() o render<Vista>():
protected function beforeRender(): void
{
// añadir un filtro
$this->template->addFilter('money', fn($val) => '$' . number_format($val, 2));
// añadir una función
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
}
En la plantilla:
<p>Price: {$price|money}</p>
{if isWeekend($now)} ... {/if}
Para lógica más compleja puede configurar directamente el objeto Latte\Engine:
protected function beforeRender(): void
{
$latte = $this->template->getLatte();
$latte->setFeature(Latte\Feature::MigrationWarnings);
}
Con atributos
Un enfoque más elegante es definir los filtros y las funciones como métodos directamente en la clase de parámetros de la plantilla del presenter o del componente, marcados con atributos:
class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
#[Latte\Attributes\TemplateFilter]
public function money(float $val): string
{
return '$' . number_format($val, 2);
}
#[Latte\Attributes\TemplateFunction]
public function isWeekend(DateTimeInterface $date): bool
{
return $date->format('N') >= 6;
}
}
Latte descubre y registra automáticamente los métodos marcados con estos atributos. El nombre del filtro o de la función en las plantillas coincide con el del método. Estos métodos deben ser públicos.
De forma global, mediante extensiones
Los enfoques anteriores encajan con filtros y funciones que solo hacen falta en presenters o componentes concretos, no en toda la aplicación. Para la aplicación entera, lo mejor es crear una extensión. Esta clase centraliza todas las extensiones de Latte de su proyecto. Un ejemplo breve:
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()),
// ...
];
}
private function filterTimeAgoInWords(DateTimeInterface $time): string
{
// ...
}
// ...
}
Registre la extensión mediante la configuración:
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Las extensiones ofrecen varias ventajas: soporte de inyección de dependencias, acceso a la capa de modelo de su aplicación y gestión centralizada de todas las extensiones. También admiten etiquetas propias, proveedores, pases del compilador y más.
Configurar todas las plantillas
El servicio TemplateFactory, que crea todas las plantillas, ofrece un array público de callbacks
$onCreate. Se llaman cada vez que se crea cualquier plantilla, así que puede configurar filtros, funciones
o variables para todas las plantillas de la aplicación desde un solo sitio. Cada callback recibe la plantilla recién creada.
Hágase inyectar el servicio TemplateFactory y registre
los callbacks, por ejemplo durante el arranque de la aplicación:
$templateFactory->onCreate[] = function (Nette\Bridges\ApplicationLatte\Template $template): void {
$template->addFilter('money', fn($val) => '$' . number_format($val, 2));
};
Traducción
Si programa una aplicación multilingüe, probablemente necesitará imprimir algunos textos de la plantilla en distintos
idiomas. Nette Framework define para ello la interfaz de traducción Nette\Localization\Translator, que tiene un
único método, translate(). Este acepta el mensaje $message, que suele ser una cadena, y cualesquiera
otros parámetros. Su tarea es devolver la cadena traducida. Nette no trae ninguna implementación predeterminada; puede elegir
entre varias soluciones ya hechas disponibles en Componette, según sus
necesidades. Su documentación explica cómo configurar el traductor.
Las plantillas se pueden configurar con un traductor que nos hacemos
pasar, mediante el método setTranslator():
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Como alternativa, el traductor se puede fijar mediante la configuración:
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Después, el traductor se puede usar, por ejemplo, como filtro |translate, incluidos los parámetros adicionales
que se pasan al método translate() (véase foo, bar):
<a href="basket">{='Basket'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
O como etiqueta de guion bajo:
<a href="basket">{_'Basket'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Para traducir una sección de la plantilla existe la etiqueta par {translate} (desde Latte 2.11; antes se usaba la
etiqueta {_}):
<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>
El traductor se llama normalmente en tiempo de ejecución, al renderizar la plantilla. La versión 3 de Latte, sin embargo, sabe traducir todos los textos estáticos ya durante la compilación de la plantilla. Esto ahorra rendimiento, porque cada cadena se traduce una sola vez y la traducción resultante se escribe en la forma compilada. Así se crean varias versiones compiladas de la plantilla en el directorio de caché, una por 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 {_$foo}, se seguirán traduciendo en tiempo de ejecución.