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-template-lookup]
--------------------------------------------

Dans les présentateurs, vous n'avez pas besoin de spécifier quel modèle doit être rendu ; le cadre détermine automatiquement le chemin, ce qui facilite le codage.

Si vous utilisez une structure de répertoires dans laquelle chaque présentateur a son propre répertoire, placez simplement le modèle dans ce répertoire sous le nom de l'action (c'est-à-dire de la vue). Par exemple, pour l'action `default`, utilisez le modèle `default.latte`:

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

Si vous utilisez une structure dans laquelle les présentateurs sont regroupés dans un répertoire et les modèles dans un dossier `templates`, enregistrez-les dans un fichier `<Presenter>.<view>.latte` soit dans un fichier `<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
\--

Le répertoire `templates` peut également être placé un niveau plus haut, au même niveau que le répertoire des classes de présentateurs.

Si le modèle n'est pas trouvé, le présentateur répond par l'[erreur 404 - page non trouvée |presenters#Error 404 etc].

Vous pouvez changer la vue en utilisant `$this->setView('anotherView')`. Il est également possible de spécifier directement le fichier de modèle avec `$this->template->setFile('/path/to/template.latte')`.

.[note]
Les fichiers dans lesquels les modèles sont recherchés peuvent être modifiés en remplaçant la méthode [formatTemplateFiles() |api:Nette\Application\UI\Presenter::formatTemplateFiles()], qui renvoie un tableau de noms de fichiers possibles.


Recherche de modèles de mise en page .[#toc-layout-template-lookup]
-------------------------------------------------------------------

Nette recherche également automatiquement le fichier de mise en page.

Si vous utilisez une structure de répertoires dans laquelle chaque présentateur a son propre répertoire, placez le modèle soit dans le dossier du présentateur, s'il lui est propre, soit à un niveau supérieur s'il est commun à plusieurs présentateurs :

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

Si vous utilisez une structure dans laquelle les présentateurs sont regroupés dans un répertoire et les modèles dans un dossier `templates`, la mise en page sera attendue aux endroits suivants :

/--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 le présentateur se trouve dans un [module |modules], il cherchera également plus haut dans l'arborescence du répertoire en fonction de l'imbrication du module.

Le nom de la présentation peut être modifié à l'aide de `$this->setLayout('layoutAdmin')` et sera alors attendu dans le fichier `@layoutAdmin.latte`. Vous pouvez également spécifier directement le fichier de modèle de présentation en utilisant `$this->setLayout('/path/to/template.latte')`.

L'utilisation de `$this->setLayout(false)` ou de la balise `{layout none}` à l'intérieur du modèle désactive la recherche de modèle.

.[note]
Les fichiers dans lesquels les modèles de présentation sont recherchés peuvent être modifiés en remplaçant la méthode [formatLayoutTemplateFiles() |api:Nette\Application\UI\Presenter::formatLayoutTemplateFiles()], qui renvoie un tableau de noms 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\UI\Article\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\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()),
			// ...
		];
	}

	// ...
}
```

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

```neon
latte:
	extensions:
		- App\UI\Accessory\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

Dans les présentateurs, vous n'avez pas besoin de spécifier quel modèle doit être rendu ; le cadre détermine automatiquement le chemin, ce qui facilite le codage.

Si vous utilisez une structure de répertoires dans laquelle chaque présentateur a son propre répertoire, placez simplement le modèle dans ce répertoire sous le nom de l'action (c'est-à-dire de la vue). Par exemple, pour l'action default, utilisez le modèle default.latte:

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

Si vous utilisez une structure dans laquelle les présentateurs sont regroupés dans un répertoire et les modèles dans un dossier templates, enregistrez-les dans un fichier <Presenter>.<view>.latte soit dans un fichier <Presenter>/<view>.latte:

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

Le répertoire templates peut également être placé un niveau plus haut, au même niveau que le répertoire des classes de présentateurs.

Si le modèle n'est pas trouvé, le présentateur répond par l'erreur 404 – page non trouvée.

Vous pouvez changer la vue en utilisant $this->setView('anotherView'). Il est également possible de spécifier directement le fichier de modèle avec $this->template->setFile('/path/to/template.latte').

Les fichiers dans lesquels les modèles sont recherchés peuvent être modifiés en remplaçant la méthode formatTemplateFiles(), qui renvoie un tableau de noms de fichiers possibles.

Recherche de modèles de mise en page

Nette recherche également automatiquement le fichier de mise en page.

Si vous utilisez une structure de répertoires dans laquelle chaque présentateur a son propre répertoire, placez le modèle soit dans le dossier du présentateur, s'il lui est propre, soit à un niveau supérieur s'il est commun à plusieurs présentateurs :

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

Si vous utilisez une structure dans laquelle les présentateurs sont regroupés dans un répertoire et les modèles dans un dossier templates, la mise en page sera attendue aux endroits suivants :

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 le présentateur se trouve dans un module, il cherchera également plus haut dans l'arborescence du répertoire en fonction de l'imbrication du module.

Le nom de la présentation peut être modifié à l'aide de $this->setLayout('layoutAdmin') et sera alors attendu dans le fichier @layoutAdmin.latte. Vous pouvez également spécifier directement le fichier de modèle de présentation en utilisant $this->setLayout('/path/to/template.latte').

L'utilisation de $this->setLayout(false) ou de la balise {layout none} à l'intérieur du modèle désactive la recherche de modèle.

Les fichiers dans lesquels les modèles de présentation sont recherchés peuvent être modifiés en remplaçant la méthode formatLayoutTemplateFiles(), qui renvoie un tableau de noms 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\UI\Article\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\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()),
			// ...
		];
	}

	// ...
}

Nous l'enregistrons en utilisant la configuration:

latte:
	extensions:
		- App\UI\Accessory\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.