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(@Nette\Localization\Translator)

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.

version: 4.0