Nette Documentation Preview

syntax
Plantillas
**********

.[perex]
Nette usa el sistema de plantillas [Latte |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}`:

```latte
<!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:

```latte
{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`:

/--pre
app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── <b>default.latte</b>
\--

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`:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>Home/</b>
        │   └── <b>default.latte</b>   ← 1.ª variante
        └── <b>Home.default.latte</b>  ← 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 |presenters#Error 404 y otros].

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')`.

.[note]
Los archivos en los que se buscan las plantillas se pueden cambiar sobrescribiendo el método [formatTemplateFiles() |api:Nette\Application\UI\Presenter::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:

/--pre
app/
└── Presentation/
    ├── <b>@layout.latte</b>           ← layout común
    └── Home/
        ├── <b>@layout.latte</b>       ← 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:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>@layout.latte</b>       ← layout común
        ├── <b>Home/</b>
        │   └── <b>@layout.latte</b>   ← solo para Home, 1.ª variante
        └── <b>Home.@layout.latte</b>  ← 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.

.[note]
Los archivos en los que se buscan las plantillas de layout se pueden cambiar sobrescribiendo el método [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::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:

```php
$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: .{data-version:3.2.9}

```php
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:

- `$basePath` es la ruta URL absoluta al directorio raíz (por ejemplo, `/eshop`)
- `$baseUrl` es la URL absoluta al directorio raíz (por ejemplo, `http://localhost/eshop`)
- `$user` es un objeto que [representa al usuario |security:authentication]
- `$presenter` es el presenter actual
- `$control` es el componente o presenter actual
- `$flashes` es un array de [mensajes |presenters#Mensajes flash] enviados con la función `flashMessage()`

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:

```php
/**
 * @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":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

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 |latte:type-system]:

```latte
{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()`:

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

.{data-version:3.3.0}
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:

```php
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í:

```latte
<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}`:

```latte
URL is: {link Home:default}
```

Encontrará más información en el capítulo [Creación de enlaces URL|creating-links].


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>()`:

```php
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:

```latte
<p>Price: {$price|money}</p>

{if isWeekend($now)} ... {/if}
```

Para lógica más compleja puede configurar directamente el objeto `Latte\Engine`:

```php
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|#Plantillas con tipos seguros] del presenter o del componente, marcados con atributos:

```php
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 |latte:extending-latte#Latte Extension]. Esta clase centraliza todas las extensiones de Latte de su proyecto. Un ejemplo breve:

```php
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 |configuration#Plantillas Latte]:

```neon
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 |dependency-injection:passing-dependencies] el servicio `TemplateFactory` y registre los callbacks, por ejemplo durante el arranque de la aplicación:

```php
$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 [api: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 |https://componette.org/search/localization], 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 |dependency-injection:passing-dependencies], mediante el método `setTranslator()`:

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

Como alternativa, el traductor se puede fijar mediante la [configuración |configuration#Plantillas Latte]:

```neon
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`):

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

O como etiqueta de guion bajo:

```latte
<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 `{_}`):

```latte
<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:

```php
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.

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:

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

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.