Nette Documentation Preview

syntax
Modèles
*******

.[perex]
Nette utilise le système de modèles [Latte |latte:]. Latte est utilisé parce que c'est le système de modèles le plus sûr pour PHP, et en même temps le système le plus intuitif. Vous n'avez pas besoin d'apprendre grand chose de nouveau, il vous suffit de connaître PHP et quelques balises Latte.

Il est courant que la page soit complétée à partir du modèle de mise en page + le modèle d'action. Voici à quoi peut ressembler un modèle de mise en page, remarquez les blocs `{block}` et la balise `{include}`:

```latte
<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>
```

Et ceci pourrait être le modèle d'action :

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

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

Il définit le bloc `content`, qui est inséré à la place de `{include content}` dans la mise en page, et redéfinit également le bloc `title`, qui écrase `{block title}` dans la mise en page. Essayez d'imaginer le résultat.


Recherche de modèles .[#toc-search-for-templates]
-------------------------------------------------

Le chemin vers les modèles est déduit selon une logique simple. Il essaie de voir si l'un de ces fichiers modèles existe par rapport au répertoire où se trouve la classe du présentateur, où `<Presenter>` est le nom du présentateur actuel et `<view>` est le nom de l'action en cours :

- `templates/<Presenter>/<view>.latte`
- `templates/<Presenter>.<view>.latte`

Si le modèle n'est pas trouvé, il essaiera de chercher dans le répertoire `templates` au niveau supérieur, c'est-à-dire au même niveau que le répertoire contenant la classe du présentateur.

Si le modèle n'y est pas trouvé non plus, la réponse est une [erreur 404 |presenters#Error 404 etc.].

Vous pouvez également changer la vue en utilisant `$this->setView('otherView')`. Ou, au lieu de chercher, spécifiez directement le nom du fichier de modèle en utilisant `$this->template->setFile('/path/to/template.latte')`.

.[note]
Vous pouvez modifier les chemins dans lesquels les modèles sont recherchés en remplaçant la méthode [formatTemplateFiles |api:Nette\Application\UI\Presenter::formatTemplateFiles()], qui renvoie un tableau de chemins de fichiers possibles.

Le modèle est attendu dans les fichiers suivants :

- `templates/<Presenter>/@<layout>.latte`
- `templates/<Presenter>.@<layout>.latte`
- `templates/@<layout>.latte` mise en page commune à plusieurs présentateurs

`<Presenter>` est le nom du présentateur actuel et `<layout>` est le nom de la mise en page, qui est par défaut `'layout'`. Le nom peut être modifié avec `$this->setLayout('otherLayout')`, de sorte que les fichiers `@otherLayout.latte` seront essayés.

Vous pouvez également spécifier directement le nom du fichier du modèle de présentation en utilisant `$this->setLayout('/path/to/template.latte')`. L'utilisation de `$this->setLayout(false)` désactivera la recherche de la mise en page.

.[note]
Vous pouvez modifier les chemins dans lesquels les modèles sont recherchés en remplaçant la méthode [formatLayoutTemplateFiles |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], qui renvoie un tableau de chemins de fichiers possibles.


Variables dans le modèle .[#toc-variables-in-the-template]
----------------------------------------------------------

Les variables sont transmises au modèle en les écrivant à `$this->template`. Elles sont ensuite disponibles dans le modèle en tant que variables locales :

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

De cette façon, nous pouvons facilement passer n'importe quelle variable aux modèles. Cependant, lors du développement d'applications robustes, il est souvent plus utile de se limiter. Par exemple, en définissant explicitement une liste de variables que le modèle attend et leurs types. Cela permettra à PHP de vérifier le type, à l'IDE d'autocompléter correctement et à l'analyse statique de détecter les erreurs.

Et comment définir une telle énumération ? Tout simplement sous la forme d'une classe et de ses propriétés. Nous la nommons de façon similaire à presenter, mais avec `Template` à la fin :

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

	// et autres variables
}
```

L'objet `$this->template` dans le présentateur sera maintenant une instance de la classe `ArticleTemplate`. Ainsi, PHP vérifie les types déclarés lorsqu'ils sont écrits. Et à partir de PHP 8.2, il préviendra également en cas d'écriture dans une variable inexistante. Dans les versions précédentes, la même chose peut être réalisée en utilisant le trait [Nette\SmartObject |utils:smartobject].

L'annotation `@property-read` est pour les IDE et l'analyse statique, elle fera fonctionner la complétion automatique, voir "PhpStorm et la complétion de code pour $this->template":https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template.

[* phpstorm-completion.webp *]

Vous pouvez aussi vous offrir le luxe de chuchoter dans les templates, il suffit d'installer le plugin Latte dans PhpStorm et de spécifier le nom de la classe au début du template, voir l'article "Latte : how to type system":https://blog.nette.org/fr/latte-comment-utiliser-le-systeme-de-type:

```latte
{templateType App\Presenters\ArticleTemplate}
...
```

C'est également de cette façon que les modèles fonctionnent dans les composants, il suffit de suivre la convention de nommage et de créer une classe de modèle `FifteenTemplate` pour le composant, par exemple `FifteenControl`.

Si vous devez créer un `$template` en tant qu'instance d'une autre classe, utilisez la méthode `createTemplate()`:

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


Variables par défaut .[#toc-default-variables]
----------------------------------------------

Les présentateurs et les composants transmettent automatiquement plusieurs variables utiles aux modèles :

- `$basePath` est un chemin URL absolu vers le répertoire racine (par exemple `/CD-collection`)
- `$baseUrl` est une URL absolue vers le répertoire racine (par exemple `http://localhost/CD-collection`)
- `$user` est un objet [représentant l'utilisateur |security:authentication]
- `$presenter` est le présentateur actuel
- `$control` est le composant ou le présentateur actuel
- `$flashes` liste des [messages |presenters#flash-messages] envoyés par la méthode `flashMessage()`

Si vous utilisez une classe de modèle personnalisée, ces variables sont transmises si vous créez une propriété pour elles.


Création de liens .[#toc-creating-links]
----------------------------------------

Dans le modèle, nous créons des liens vers d'autres présentateurs et actions comme suit :

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

L'attribut `n:href` est très pratique pour les balises HTML `<a>`. Si nous voulons imprimer le lien ailleurs, par exemple dans le texte, nous utilisons `{link}`:

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

Pour plus d'informations, voir [Création de liens |Creating Links].


Filtres, balises, etc. personnalisés .[#toc-custom-filters-tags-etc]
--------------------------------------------------------------------

Le système de modélisation Latte peut être étendu avec des filtres, des fonctions, des balises, etc. personnalisés. Ceci peut être fait directement dans la méthode `render<View>` ou `beforeRender()`:

```php
public function beforeRender(): void
{
	// ajout d'un filtre
	$this->template->addFilter('foo', /* ... */);

	// ou configurer directement l'objet Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}
```

Latte version 3 propose un moyen plus avancé en créant une [extension |latte:creating-extension] pour chaque projet web. Voici un exemple approximatif d'une telle classe :

```php
namespace App\Templating;

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()),
			// ...
		];
	}

	// ...
}
```

Nous l'enregistrons en utilisant la [configuration |configuration#Latte]:

```neon
latte:
	extensions:
		- App\Templating\LatteExtension
```


Traduction de .[#toc-translating]
---------------------------------

Si vous programmez une application multilingue, vous aurez probablement besoin d'éditer une partie du texte du modèle dans différentes langues. Pour ce faire, le Nette Framework définit une interface de traduction [api:Nette\Localization\Translator], qui possède une seule méthode `translate()`. Celle-ci accepte le message `$message`, qui est généralement une chaîne de caractères, et tout autre paramètre. La tâche consiste à renvoyer la chaîne traduite.
Il n'y a pas d'implémentation par défaut dans Nette, vous pouvez choisir en fonction de vos besoins parmi plusieurs solutions prêtes à l'emploi que vous trouverez sur [Componette |https://componette.org/search/localization]. Leur documentation vous indique comment configurer le traducteur.

Les modèles peuvent être configurés avec un traducteur, que l'on nous [aura passé |dependency-injection:passing-dependencies], en utilisant la méthode `setTranslator()`:

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

Alternativement, le traducteur peut être défini à l'aide de la [configuration |configuration#Latte]:

```neon
latte:
	extensions:
		- Latte\Essential\TranslatorExtension
```

Le traducteur peut alors être utilisé, par exemple, comme un filtre `|translate`, avec des paramètres supplémentaires transmis à la méthode `translate()` (voir `foo, bar`) :

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

Ou comme une balise de soulignement :

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

Pour la traduction des sections de modèles, il existe une balise appariée `{translate}` (depuis Latte 2.11, la balise `{_}` était utilisée auparavant) :

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

Translator est appelé par défaut au moment de l'exécution, lors du rendu du modèle. Latte version 3, cependant, peut traduire tout le texte statique pendant la compilation du modèle. Cela permet de gagner en performance car chaque chaîne n'est traduite qu'une seule fois et la traduction résultante est écrite dans le modèle compilé. Cela crée plusieurs versions compilées du modèle dans le répertoire de cache, une pour chaque langue. Pour ce faire, il suffit de spécifier la langue comme deuxième paramètre :

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

Par texte statique, nous entendons, par exemple, `{_'hello'}` ou `{translate}hello{/translate}`. Le texte non statique, tel que `{_$foo}`, continuera à être compilé à la volée.

Modèles

Nette utilise le système de modèles Latte. Latte est utilisé parce que c'est le système de modèles le plus sûr pour PHP, et en même temps le système le plus intuitif. Vous n'avez pas besoin d'apprendre grand chose de nouveau, il vous suffit de connaître PHP et quelques balises Latte.

Il est courant que la page soit complétée à partir du modèle de mise en page + le modèle d'action. Voici à quoi peut ressembler un modèle de mise en page, remarquez les blocs {block} et la balise {include}:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

Et ceci pourrait être le modèle d'action :

{block title}Homepage{/block}

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

Il définit le bloc content, qui est inséré à la place de {include content} dans la mise en page, et redéfinit également le bloc title, qui écrase {block title} dans la mise en page. Essayez d'imaginer le résultat.

Recherche de modèles

Le chemin vers les modèles est déduit selon une logique simple. Il essaie de voir si l'un de ces fichiers modèles existe par rapport au répertoire où se trouve la classe du présentateur, où <Presenter> est le nom du présentateur actuel et <view> est le nom de l'action en cours :

  • templates/<Presenter>/<view>.latte
  • templates/<Presenter>.<view>.latte

Si le modèle n'est pas trouvé, il essaiera de chercher dans le répertoire templates au niveau supérieur, c'est-à-dire au même niveau que le répertoire contenant la classe du présentateur.

Si le modèle n'y est pas trouvé non plus, la réponse est une erreur 404.

Vous pouvez également changer la vue en utilisant $this->setView('otherView'). Ou, au lieu de chercher, spécifiez directement le nom du fichier de modèle en utilisant $this->template->setFile('/path/to/template.latte').

Vous pouvez modifier les chemins dans lesquels les modèles sont recherchés en remplaçant la méthode formatTemplateFiles, qui renvoie un tableau de chemins de fichiers possibles.

Le modèle est attendu dans les fichiers suivants :

  • templates/<Presenter>/@<layout>.latte
  • templates/<Presenter>.@<layout>.latte
  • templates/@<layout>.latte mise en page commune à plusieurs présentateurs

<Presenter> est le nom du présentateur actuel et <layout> est le nom de la mise en page, qui est par défaut 'layout'. Le nom peut être modifié avec $this->setLayout('otherLayout'), de sorte que les fichiers @otherLayout.latte seront essayés.

Vous pouvez également spécifier directement le nom du fichier du modèle de présentation en utilisant $this->setLayout('/path/to/template.latte'). L'utilisation de $this->setLayout(false) désactivera la recherche de la mise en page.

Vous pouvez modifier les chemins dans lesquels les modèles sont recherchés en remplaçant la méthode formatLayoutTemplateFiles, qui renvoie un tableau de chemins de fichiers possibles.

Variables dans le modèle

Les variables sont transmises au modèle en les écrivant à $this->template. Elles sont ensuite disponibles dans le modèle en tant que variables locales :

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

De cette façon, nous pouvons facilement passer n'importe quelle variable aux modèles. Cependant, lors du développement d'applications robustes, il est souvent plus utile de se limiter. Par exemple, en définissant explicitement une liste de variables que le modèle attend et leurs types. Cela permettra à PHP de vérifier le type, à l'IDE d'autocompléter correctement et à l'analyse statique de détecter les erreurs.

Et comment définir une telle énumération ? Tout simplement sous la forme d'une classe et de ses propriétés. Nous la nommons de façon similaire à presenter, mais avec Template à la fin :

/**
 * @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;

	// et autres variables
}

L'objet $this->template dans le présentateur sera maintenant une instance de la classe ArticleTemplate. Ainsi, PHP vérifie les types déclarés lorsqu'ils sont écrits. Et à partir de PHP 8.2, il préviendra également en cas d'écriture dans une variable inexistante. Dans les versions précédentes, la même chose peut être réalisée en utilisant le trait Nette\SmartObject.

L'annotation @property-read est pour les IDE et l'analyse statique, elle fera fonctionner la complétion automatique, voir PhpStorm et la complétion de code pour $this->template.

Vous pouvez aussi vous offrir le luxe de chuchoter dans les templates, il suffit d'installer le plugin Latte dans PhpStorm et de spécifier le nom de la classe au début du template, voir l'article Latte : how to type system:

{templateType App\Presenters\ArticleTemplate}
...

C'est également de cette façon que les modèles fonctionnent dans les composants, il suffit de suivre la convention de nommage et de créer une classe de modèle FifteenTemplate pour le composant, par exemple FifteenControl.

Si vous devez créer un $template en tant qu'instance d'une autre classe, utilisez la méthode createTemplate():

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

Variables par défaut

Les présentateurs et les composants transmettent automatiquement plusieurs variables utiles aux modèles :

  • $basePath est un chemin URL absolu vers le répertoire racine (par exemple /CD-collection)
  • $baseUrl est une URL absolue vers le répertoire racine (par exemple http://localhost/CD-collection)
  • $user est un objet représentant l'utilisateur
  • $presenter est le présentateur actuel
  • $control est le composant ou le présentateur actuel
  • $flashes liste des messages envoyés par la méthode flashMessage()

Si vous utilisez une classe de modèle personnalisée, ces variables sont transmises si vous créez une propriété pour elles.

Dans le modèle, nous créons des liens vers d'autres présentateurs et actions comme suit :

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

L'attribut n:href est très pratique pour les balises HTML <a>. Si nous voulons imprimer le lien ailleurs, par exemple dans le texte, nous utilisons {link}:

URL is: {link Home:default}

Pour plus d'informations, voir Création de liens.

Filtres, balises, etc. personnalisés

Le système de modélisation Latte peut être étendu avec des filtres, des fonctions, des balises, etc. personnalisés. Ceci peut être fait directement dans la méthode render<View> ou beforeRender():

public function beforeRender(): void
{
	// ajout d'un filtre
	$this->template->addFilter('foo', /* ... */);

	// ou configurer directement l'objet Latte\Engine
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte version 3 propose un moyen plus avancé en créant une extension pour chaque projet web. Voici un exemple approximatif d'une telle classe :

namespace App\Templating;

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()),
			// ...
		];
	}

	// ...
}

Nous l'enregistrons en utilisant la configuration:

latte:
	extensions:
		- App\Templating\LatteExtension

Traduction de

Si vous programmez une application multilingue, vous aurez probablement besoin d'éditer une partie du texte du modèle dans différentes langues. Pour ce faire, le Nette Framework définit une interface de traduction Nette\Localization\Translator, qui possède une seule méthode translate(). Celle-ci accepte le message $message, qui est généralement une chaîne de caractères, et tout autre paramètre. La tâche consiste à renvoyer la chaîne traduite. Il n'y a pas d'implémentation par défaut dans Nette, vous pouvez choisir en fonction de vos besoins parmi plusieurs solutions prêtes à l'emploi que vous trouverez sur Componette. Leur documentation vous indique comment configurer le traducteur.

Les modèles peuvent être configurés avec un traducteur, que l'on nous aura passé, en utilisant la méthode setTranslator():

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

Alternativement, le traducteur peut être défini à l'aide de la configuration:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension

Le traducteur peut alors être utilisé, par exemple, comme un filtre |translate, avec des paramètres supplémentaires transmis à la méthode translate() (voir foo, bar) :

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

Ou comme une balise de soulignement :

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

Pour la traduction des sections de modèles, il existe une balise appariée {translate} (depuis Latte 2.11, la balise {_} était utilisée auparavant) :

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

Translator est appelé par défaut au moment de l'exécution, lors du rendu du modèle. Latte version 3, cependant, peut traduire tout le texte statique pendant la compilation du modèle. Cela permet de gagner en performance car chaque chaîne n'est traduite qu'une seule fois et la traduction résultante est écrite dans le modèle compilé. Cela crée plusieurs versions compilées du modèle dans le répertoire de cache, une pour chaque langue. Pour ce faire, il suffit de spécifier la langue comme deuxième paramètre :

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

Par texte statique, nous entendons, par exemple, {_'hello'} ou {translate}hello{/translate}. Le texte non statique, tel que {_$foo}, continuera à être compilé à la volée.