Nette Documentation Preview

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

.[perex]
Nette utiliza el sistema de plantillas [Latte |latte:]. Se utiliza Latte porque es el sistema de plantillas más seguro para PHP, y al mismo tiempo el sistema más intuitivo. Usted no tiene que aprender mucho nuevo, sólo necesita saber PHP y algunas etiquetas Latte.

Lo habitual es que la página se complete a partir de la plantilla layout + la plantilla action. Este es el aspecto que podría tener una plantilla de maquetación, fíjate en los bloques `{block}` y 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 podría ser la plantilla de acción:

```latte
{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}
```

Define el bloque `content`, que se inserta en lugar de `{include content}` en el diseño, y también redefine el bloque `title`, que sobrescribe `{block title}` en el diseño. Intenta imaginar el resultado.


Búsqueda de plantillas .[#toc-template-lookup]
----------------------------------------------

En los presentadores, no es necesario especificar qué plantilla debe renderizarse; el framework determinará automáticamente la ruta, facilitándole la codificación.

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

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

Si utiliza una estructura en la que los presentadores están juntos en un directorio y las plantillas en una carpeta `templates`, guárdela en un archivo `<Presenter>.<view>.latte` o en `<Presenter>/<view>.latte`:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>Home.default.latte</b>  ← 1st variant
        └── <b>Home/</b>
            └── <b>default.latte</b>   ← 2nd variant
\--

El directorio `templates` también puede colocarse un nivel más arriba, al mismo nivel que el directorio con las clases de presentador.

Si no se encuentra la plantilla, el presentador responde con el [error 404 - página no encontrada |presenters#Error 404 etc].

Puede cambiar la vista utilizando `$this->setView('anotherView')`. También es posible especificar directamente el archivo de plantilla con `$this->template->setFile('/path/to/template.latte')`.

.[note]
Los archivos en los que se buscan las plantillas pueden cambiarse anulando el método [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()], que devuelve una matriz de posibles nombres de archivo.


Búsqueda de plantillas de diseño .[#toc-layout-template-lookup]
---------------------------------------------------------------

Nette también busca automáticamente el archivo de diseño.

Si utiliza una estructura de directorios en la que cada presentador tiene su propio directorio, coloque la maqueta en la carpeta con el presentador, si es específica sólo para él, o en un nivel superior si es común a varios presentadores:

/--pre
app/
└── UI/
    ├── <b>@layout.latte</b>           ← common layout
    └── Home/
        ├── <b>@layout.latte</b>       ← only for Home presenter
        ├── HomePresenter.php
        └── default.latte
\--

Si utiliza una estructura en la que los presentadores están agrupados en un directorio y las plantillas se encuentran en una carpeta `templates`, la maquetación se esperará en los siguientes lugares:

/--pre
app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── <b>@layout.latte</b>       ← common layout
        ├── <b>Home.@layout.latte</b>  ← only for Home, 1st variant
        └── <b>Home/</b>
            └── <b>@layout.latte</b>   ← only for Home, 2nd variant
\--

Si el presentador está en un [módulo |modules], también buscará más arriba en el árbol de directorios según el anidamiento del módulo.

El nombre de la presentación puede cambiarse utilizando `$this->setLayout('layoutAdmin')` y entonces se esperará en el archivo `@layoutAdmin.latte`. También puede especificar directamente el archivo de plantilla de presentación utilizando `$this->setLayout('/path/to/template.latte')`.

El uso de `$this->setLayout(false)` o de la etiqueta `{layout none}` dentro de la plantilla desactiva la búsqueda de diseños.

.[note]
Los archivos en los que se buscan las plantillas de diseño pueden modificarse modificando el método [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], que devuelve una matriz de posibles nombres de archivo.


Variables en la plantilla .[#toc-variables-in-the-template]
-----------------------------------------------------------

Las variables se pasan a la plantilla escribiéndolas en `$this->template` y luego están disponibles en la plantilla como variables locales:

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

De esta forma podemos pasar fácilmente cualquier variable a las plantillas. Sin embargo, cuando desarrollamos aplicaciones robustas, a menudo es más útil limitarnos. Por ejemplo, definiendo explícitamente una lista de variables que la plantilla espera y sus tipos. Esto permitirá a PHP comprobar los tipos, al IDE autocompletar correctamente, y al análisis estático detectar errores.

¿Y cómo definimos tal enumeración? Simplemente en forma de una clase y sus propiedades. La nombramos de forma similar a presenter, pero 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` en el presentador será ahora una instancia de la clase `ArticleTemplate`. Así que PHP comprobará los tipos declarados cuando se escriban. Y a partir de PHP 8.2 también advertirá sobre la escritura en una variable no existente, en versiones anteriores se puede lograr lo mismo usando el rasgo [Nette\SmartObject |utils:smartobject].

La anotación `@property-read` es para IDE y análisis estático, hará que funcione el autocompletado, vea "PhpStorm y el completado de código para $this->template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

Puedes permitirte el lujo de susurrar en las plantillas también, simplemente instala el plugin Latte en PhpStorm y especifica el nombre de la clase al principio de la plantilla, ver el artículo "Latte: cómo escribir sistema:https://blog.nette.org/es/latte-como-utilizar-el-sistema-de-tipos":

```latte
{templateType App\UI\Article\ArticleTemplate}
...
```

Así es también como funcionan las plantillas en los componentes, sólo tienes que seguir la convención de nomenclatura y crear una clase de plantilla `FifteenTemplate` para el componente, por ejemplo `FifteenControl`.

Si necesitas crear un `$template` como una instancia de otra clase, utiliza el método `createTemplate()`:

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


Variables por defecto .[#toc-default-variables]
-----------------------------------------------

Los presentadores y componentes pasan varias variables útiles a las plantillas de forma automática:

- `$basePath` es una ruta URL absoluta al directorio raíz (por ejemplo `/CD-collection`)
- `$baseUrl` es una URL absoluta al directorio raíz (por ejemplo `http://localhost/CD-collection`)
- `$user` es un objeto [que representa al usuario |security:authentication]
- `$presenter` es el presentador actual
- `$control` es el componente o presentador actual
- `$flashes` lista de [mensajes |presenters#flash-messages] enviados por el método `flashMessage()`

Si utilizas una clase de plantilla personalizada, estas variables se pasan si creas una propiedad para ellas.


Creación de enlaces .[#toc-creating-links]
------------------------------------------

En la plantilla creamos enlaces a otros presentadores y acciones de la siguiente manera:

```latte
<a n:href="Product:show">detail</a>
```

Atributo `n:href` es muy útil para etiquetas HTML `<a>`. Si queremos imprimir el enlace en otro lugar, por ejemplo en el texto, utilizamos `{link}`:

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

Para más información, véase [Creación de enlaces |Creating Links].


Filtros personalizados, etiquetas, etc. .[#toc-custom-filters-tags-etc]
-----------------------------------------------------------------------

El sistema de plantillas Latte puede ampliarse con filtros personalizados, funciones, etiquetas, etc. Esto puede hacerse directamente en el método `render<View>` o en el método `beforeRender()`:

```php
public function beforeRender(): void
{
	// adding a filter
	$this->template->addFilter('foo', /* ... */);

	// or configure the Latte\Engine object directly
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}
```

La versión 3 de Latte ofrece una forma más avanzada creando una [extensión |latte:creating-extension] para cada proyecto web. He aquí un ejemplo aproximado de una clase de este tipo:

```php
namespace App\UI\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 [configuration|configuration#Latte]:

```neon
latte:
	extensions:
		- App\UI\Accessory\LatteExtension
```


Traducir .[#toc-translating]
----------------------------

Si estás programando una aplicación multilingüe, es probable que necesites mostrar parte del texto de la plantilla en diferentes idiomas. Para ello, Nette Framework define una interfaz de traducción [api:Nette\Localization\Translator], que tiene un único método `translate()`. Éste acepta el mensaje `$message`, que normalmente es una cadena, y cualquier otro parámetro. La tarea consiste en devolver la cadena traducida.
No existe una implementación por defecto en Nette, puede elegir según sus necesidades entre varias soluciones ya preparadas que puede encontrar en [Componette |https://componette.org/search/localization]. Su documentación le indica cómo configurar el traductor.

Las plantillas se pueden configurar con un traductor, que [nos hab |dependency-injection:passing-dependencies]rán pasado, utilizando el método `setTranslator()`:

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

Alternativamente, el traductor se puede establecer utilizando la [configuración |configuration#Latte]:

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

El traductor puede utilizarse, por ejemplo, como un filtro `|translate`, con parámetros adicionales pasados 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 una etiqueta de subrayado:

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

Para la traducción de secciones de plantillas, existe una etiqueta emparejada `{translate}` (desde Latte 2.11, antes se utilizaba la etiqueta `{_}` ):

```latte
<a href="order">{translate}Order{/translate}</a>
<a href="order">{translate foo, bar}Order{/translate}</a>
```

Translator se llama por defecto en tiempo de ejecución al renderizar la plantilla. La versión 3 de Latte, sin embargo, puede traducir todo el texto estático durante la compilación de la plantilla. Esto ahorra rendimiento porque cada cadena se traduce sólo una vez y la traducción resultante se escribe en el formulario compilado. Esto crea múltiples versiones compiladas de la plantilla en el directorio caché, una para cada idioma. Para ello, sólo tiene que especificar el idioma como segundo parámetro:

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

Por texto estático entendemos, por ejemplo, `{_'hello'}` o `{translate}hello{/translate}`. El texto no estático, como `{_$foo}`, seguirá compilándose sobre la marcha.

Plantillas

Nette utiliza el sistema de plantillas Latte. Se utiliza Latte porque es el sistema de plantillas más seguro para PHP, y al mismo tiempo el sistema más intuitivo. Usted no tiene que aprender mucho nuevo, sólo necesita saber PHP y algunas etiquetas Latte.

Lo habitual es que la página se complete a partir de la plantilla layout + la plantilla action. Este es el aspecto que podría tener una plantilla de maquetación, fíjate en los bloques {block} y 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 podría ser la plantilla de acción:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

Define el bloque content, que se inserta en lugar de {include content} en el diseño, y también redefine el bloque title, que sobrescribe {block title} en el diseño. Intenta imaginar el resultado.

Búsqueda de plantillas

En los presentadores, no es necesario especificar qué plantilla debe renderizarse; el framework determinará automáticamente la ruta, facilitándole la codificación.

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

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

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

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1st variant
        └── Home/
            └── default.latte   ← 2nd variant

El directorio templates también puede colocarse un nivel más arriba, al mismo nivel que el directorio con las clases de presentador.

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

Puede cambiar la vista utilizando $this->setView('anotherView'). También es posible especificar directamente el archivo de plantilla con $this->template->setFile('/path/to/template.latte').

Los archivos en los que se buscan las plantillas pueden cambiarse anulando el método formatTemplateFiles(), que devuelve una matriz de posibles nombres de archivo.

Búsqueda de plantillas de diseño

Nette también busca automáticamente el archivo de diseño.

Si utiliza una estructura de directorios en la que cada presentador tiene su propio directorio, coloque la maqueta en la carpeta con el presentador, si es específica sólo para él, o en un nivel superior si es común a varios presentadores:

app/
└── UI/
    ├── @layout.latte           ← common layout
    └── Home/
        ├── @layout.latte       ← only for Home presenter
        ├── HomePresenter.php
        └── default.latte

Si utiliza una estructura en la que los presentadores están agrupados en un directorio y las plantillas se encuentran en una carpeta templates, la maquetación se esperará en los siguientes lugares:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← common layout
        ├── Home.@layout.latte  ← only for Home, 1st variant
        └── Home/
            └── @layout.latte   ← only for Home, 2nd variant

Si el presentador está en un módulo, también buscará más arriba en el árbol de directorios según el anidamiento del módulo.

El nombre de la presentación puede cambiarse utilizando $this->setLayout('layoutAdmin') y entonces se esperará en el archivo @layoutAdmin.latte. También puede especificar directamente el archivo de plantilla de presentación utilizando $this->setLayout('/path/to/template.latte').

El uso de $this->setLayout(false) o de la etiqueta {layout none} dentro de la plantilla desactiva la búsqueda de diseños.

Los archivos en los que se buscan las plantillas de diseño pueden modificarse modificando el método formatLayoutTemplateFiles(), que devuelve una matriz de posibles nombres de archivo.

Variables en la plantilla

Las variables se pasan a la plantilla escribiéndolas en $this->template y luego están disponibles en la plantilla como variables locales:

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

De esta forma podemos pasar fácilmente cualquier variable a las plantillas. Sin embargo, cuando desarrollamos aplicaciones robustas, a menudo es más útil limitarnos. Por ejemplo, definiendo explícitamente una lista de variables que la plantilla espera y sus tipos. Esto permitirá a PHP comprobar los tipos, al IDE autocompletar correctamente, y al análisis estático detectar errores.

¿Y cómo definimos tal enumeración? Simplemente en forma de una clase y sus propiedades. La nombramos de forma similar a presenter, pero 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 presentador será ahora una instancia de la clase ArticleTemplate. Así que PHP comprobará los tipos declarados cuando se escriban. Y a partir de PHP 8.2 también advertirá sobre la escritura en una variable no existente, en versiones anteriores se puede lograr lo mismo usando el rasgo Nette\SmartObject.

La anotación @property-read es para IDE y análisis estático, hará que funcione el autocompletado, vea PhpStorm y el completado de código para $this->template.

Puedes permitirte el lujo de susurrar en las plantillas también, simplemente instala el plugin Latte en PhpStorm y especifica el nombre de la clase al principio de la plantilla, ver el artículo „Latte: cómo escribir sistema:https://blog.nette.org/…ema-de-tipos“:

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

Así es también como funcionan las plantillas en los componentes, sólo tienes que seguir la convención de nomenclatura y crear una clase de plantilla FifteenTemplate para el componente, por ejemplo FifteenControl.

Si necesitas crear un $template como una instancia de otra clase, utiliza el método createTemplate():

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

Variables por defecto

Los presentadores y componentes pasan varias variables útiles a las plantillas de forma automática:

  • $basePath es una ruta URL absoluta al directorio raíz (por ejemplo /CD-collection)
  • $baseUrl es una URL absoluta al directorio raíz (por ejemplo http://localhost/CD-collection)
  • $user es un objeto que representa al usuario
  • $presenter es el presentador actual
  • $control es el componente o presentador actual
  • $flashes lista de mensajes enviados por el método flashMessage()

Si utilizas una clase de plantilla personalizada, estas variables se pasan si creas una propiedad para ellas.

En la plantilla creamos enlaces a otros presentadores y acciones de la siguiente manera:

<a n:href="Product:show">detail</a>

Atributo n:href es muy útil para etiquetas HTML <a>. Si queremos imprimir el enlace en otro lugar, por ejemplo en el texto, utilizamos {link}:

URL is: {link Home:default}

Para más información, véase Creación de enlaces.

Filtros personalizados, etiquetas, etc.

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

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

	// or configure the Latte\Engine object directly
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

La versión 3 de Latte ofrece una forma más avanzada creando una extensión para cada proyecto web. He aquí un ejemplo aproximado de una clase de este tipo:

namespace App\UI\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 configuration:

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

Traducir

Si estás programando una aplicación multilingüe, es probable que necesites mostrar parte del texto de la plantilla en diferentes idiomas. Para ello, Nette Framework define una interfaz de traducción Nette\Localization\Translator, que tiene un único método translate(). Éste acepta el mensaje $message, que normalmente es una cadena, y cualquier otro parámetro. La tarea consiste en devolver la cadena traducida. No existe una implementación por defecto en Nette, puede elegir según sus necesidades entre varias soluciones ya preparadas que puede encontrar en Componette. Su documentación le indica cómo configurar el traductor.

Las plantillas se pueden configurar con un traductor, que nos habrán pasado, utilizando el método setTranslator():

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

Alternativamente, el traductor se puede establecer utilizando la configuración:

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

El traductor puede utilizarse, por ejemplo, como un filtro |translate, con parámetros adicionales pasados 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 una etiqueta de subrayado:

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

Para la traducción de secciones de plantillas, existe una etiqueta emparejada {translate} (desde Latte 2.11, antes se utilizaba la etiqueta {_} ):

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

Translator se llama por defecto en tiempo de ejecución al renderizar la plantilla. La versión 3 de Latte, sin embargo, puede traducir todo el texto estático durante la compilación de la plantilla. Esto ahorra rendimiento porque cada cadena se traduce sólo una vez y la traducción resultante se escribe en el formulario compilado. Esto crea múltiples versiones compiladas de la plantilla en el directorio caché, una para cada idioma. Para ello, sólo tiene que especificar el idioma como segundo parámetro:

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

Por texto estático entendemos, por ejemplo, {_'hello'} o {translate}hello{/translate}. El texto no estático, como {_$foo}, seguirá compilándose sobre la marcha.