Templates
Nette utilise le système de templates Latte. D'une part parce que c'est le système de templates le plus sécurisé pour PHP, et d'autre part parce que c'est aussi le système le plus intuitif. Vous n'avez pas besoin d'apprendre beaucoup de nouveautés, la connaissance de PHP et de quelques balises suffit.
Il est courant qu'une page soit composée d'un template de layout + du template de l'action donnée. Voici à quoi peut
ressembler un template de layout, remarquez les blocs {block} et la balise {include} :
<!DOCTYPE html>
<html>
<head>
<title>{block title}Mon App{/block}</title>
</head>
<body>
<header>...</header>
{include content}
<footer>...</footer>
</body>
</html>
Et voici ce que sera le template de l'action :
{block title}Page d'accueil{/block}
{block content}
<h1>Page d'accueil</h1>
...
{/block}
Il définit le bloc content, qui sera inséré à la place de {include content} dans le layout, et
redéfinit également le bloc title, qui écrasera {block title} dans le layout. Essayez d'imaginer le
résultat.
Recherche de templates
Vous n'avez pas besoin de spécifier dans les presenters quel template doit être rendu, le framework déduit le chemin lui-même et vous évite d'écrire.
Si vous utilisez une structure de répertoires où chaque presenter a son propre répertoire, placez simplement le template
dans ce répertoire sous le nom de l'action (ou de la vue), c'est-à-dire pour l'action default, utilisez le template
default.latte :
app/
└── Presentation/
└── Home/
├── HomePresenter.php
└── default.latte
Si vous utilisez une structure où les presenters sont regroupés dans un seul répertoire et les templates dans un dossier
templates, enregistrez-le soit dans le fichier <Presenter>.<view>.latte soit
<Presenter>/<view>.latte :
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── Home.default.latte ← 1ère variante
└── Home/
└── default.latte ← 2ème variante
Le répertoire templates peut également être placé un niveau plus haut, c'est-à-dire au même niveau que le
répertoire contenant les classes des presenters.
Si le template n'est pas trouvé, le presenter répondra par une erreur 404 – page non trouvée.
Vous pouvez changer la vue en utilisant $this->setView('autreVue'). Il est également possible de spécifier
directement le fichier de template en utilisant $this->template->setFile('/chemin/vers/template.latte').
Les fichiers où les templates sont recherchés peuvent être modifiés en surchargeant la méthode formatTemplateFiles(), qui retourne un tableau de noms de fichiers possibles.
Recherche du template de layout
Nette recherche également automatiquement le fichier de layout.
Si vous utilisez une structure de répertoires où chaque presenter a son propre répertoire, placez le layout soit dans le dossier du presenter s'il lui est spécifique, soit un niveau plus haut s'il est commun à plusieurs presenters :
app/
└── Presentation/
├── @layout.latte ← layout commun
└── Home/
├── @layout.latte ← uniquement pour le presenter Home
├── HomePresenter.php
└── default.latte
Si vous utilisez une structure où les presenters sont regroupés dans un seul répertoire et les templates dans un dossier
templates, le layout sera attendu à ces endroits :
app/
└── Presenters/
├── HomePresenter.php
└── templates/
├── @layout.latte ← layout commun
├── Home.@layout.latte ← uniquement pour Home, 1ère variante
└── Home/
└── @layout.latte ← uniquement pour Home, 2ème variante
Si le presenter se trouve dans un module, la recherche s'effectuera également aux niveaux de répertoires supérieurs, en fonction de l'imbrication du module.
Le nom du layout peut être modifié à l'aide de $this->setLayout('layoutAdmin') et il sera alors attendu dans
le fichier @layoutAdmin.latte. Il est également possible de spécifier directement le fichier de template de layout
à l'aide de $this->setLayout('/chemin/vers/template.latte').
En utilisant $this->setLayout(false) ou la balise {layout none} à l'intérieur du template, la
recherche de layout est désactivée.
Les fichiers où les templates de layout sont recherchés peuvent être modifiés en surchargeant la méthode formatLayoutTemplateFiles(), qui retourne un tableau de noms de fichiers possibles.
Variables dans le template
Les variables sont passées aux templates en les écrivant dans $this->template. Elles deviennent alors
disponibles dans le template comme variables locales :
$this->template->article = $this->articles->getById($id);
Pour passer automatiquement la valeur d'une propriété au template sous forme de variable, marquez-la de
l'attribut #[TemplateVariable] et rendez-la publique :
use Nette\Application\Attributes\TemplateVariable;
class ArticlePresenter extends Nette\Application\UI\Presenter
{
#[TemplateVariable]
public string $siteName = 'My blog';
}
Si vous passez au template une variable du même nom, #[TemplateVariable] ne l'écrasera pas.
Variables par défaut
Les presenters et les composants transmettent automatiquement plusieurs variables utiles aux templates :
$basePathest le chemin URL absolu vers le répertoire racine (par ex./eshop)$baseUrlest l'URL absolue vers le répertoire racine (par ex.http://localhost/eshop)$userest l'objet représentant l'utilisateur$presenterest le presenter actuel$controlest le composant ou presenter actuel$flashestableau des messages envoyés par la fonctionflashMessage()
Si vous utilisez votre propre classe de template, ces variables seront transmises si vous créez une propriété pour elles.
Templates typés
Quand vous développez des applications robustes, il est utile de définir explicitement quelles variables le template attend et de quels types. Cela apporte le contrôle de types de PHP, des suggestions pertinentes dans l'IDE et permet à l'analyse statique de détecter les erreurs.
Comment définir une telle liste ? Simplement sous forme de classe dont les propriétés représentent les variables du
template. Nommez-la comme le presenter, en ajoutant simplement 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 d'autres variables
}
L'objet $this->template du presenter sera désormais une instance de la classe ArticleTemplate.
PHP contrôlera donc les types déclarés lors de l'écriture.
Nette choisit la classe de template automatiquement. Il cherche d'abord une classe nommée
<Presenter><Action>Template, par ex. ArticleEditTemplate pour l'action edit, et
ne se rabat sur <Presenter>Template que si elle n'existe pas.
L'annotation @property-read est destinée à l'IDE et à l'analyse statique : elle active la complétion, voyez PhpStorm et la complétion pour
$this->template.

Vous pouvez aussi utiliser la complétion directement dans les templates. Il suffit d'installer le plugin Latte pour PhpStorm et d'indiquer au début du template le nom de la classe de paramètres, plus de détails dans le chapitre Latte : système de types :
{templateType App\Presentation\Article\ArticleTemplate}
...
Cela vaut également pour les composants. Il suffit de suivre la convention de nommage et de créer une classe de paramètres
FifteenTemplate pour un composant comme FifteenControl.
Si vous avez besoin d'une autre classe de paramètres, utilisez la méthode createTemplate() :
public function renderDefault(): void
{
$template = $this->createTemplate(SpecialTemplate::class);
$template->foo = 123;
// ...
$this->sendTemplate($template);
}
Si vous avez besoin d'influencer la façon dont le template est finalisé avant le rendu – par exemple
pour ajouter des variables partagées par toutes les actions – vous pouvez redéfinir la méthode
completeTemplate() dans le presenter. Elle est appelée juste avant le rendu du template :
protected function completeTemplate(Nette\Application\UI\Template $template): void
{
parent::completeTemplate($template);
$template->siteName = 'My blog';
}
Création de liens
Dans le template, les liens vers d'autres presenters & actions sont créés de cette manière :
<a n:href="Product:show">détail du produit</a>
L'attribut n:href est très pratique pour les balises HTML <a>. Si nous voulons afficher le
lien ailleurs, par exemple dans du texte, nous utilisons {link} :
L'adresse est : {link Home:default}
Plus d'informations peuvent être trouvées dans le chapitre Création de liens URL.
Filtres personnalisés, balises, etc.
Le système de templates Latte peut être étendu avec des filtres, des fonctions, des balises et d'autres éléments personnalisés. Trois approches sont possibles, de la solution ad hoc rapide aux motifs architecturaux valables pour toute l'application.
Ad hoc, dans les méthodes du presenter
L'approche la plus rapide consiste à ajouter les filtres ou les fonctions directement dans le code du presenter ou du
composant. Dans les presenters, les méthodes beforeRender() ou render<View>() s'y prêtent
bien :
protected function beforeRender(): void
{
// ajout d'un filtre
$this->template->addFilter('money', fn($val) => '$' . number_format($val, 2));
// ajout d'une fonction
$this->template->addFunction('isWeekend', fn($date) => $date->format('N') >= 6);
}
Dans le template :
<p>Price: {$price|money}</p>
{if isWeekend($now)} ... {/if}
Pour une logique plus complexe, vous pouvez configurer directement l'objet Latte\Engine :
protected function beforeRender(): void
{
$latte = $this->template->getLatte();
$latte->setFeature(Latte\Feature::MigrationWarnings);
}
À l'aide d'attributs
Une approche plus élégante consiste à définir les filtres et les fonctions comme méthodes directement dans la classe de paramètres de template du presenter ou du composant, marquées par des attributs :
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 découvre et enregistre automatiquement les méthodes marquées par ces attributs. Le nom du filtre ou de la fonction dans les templates correspond au nom de la méthode. Ces méthodes doivent être publiques.
Globalement, à l'aide d'extensions
Les approches précédentes conviennent aux filtres et fonctions dont vous n'avez besoin que dans certains presenters ou composants, pas dans toute l'application. Pour l'application entière, le mieux est de créer une extension. Cette classe centralise toutes les extensions de Latte de votre projet. Exemple succinct :
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
{
// ...
}
// ...
}
Enregistrez l'extension via la configuration :
latte:
extensions:
- App\Presentation\Accessory\LatteExtension
Les extensions offrent plusieurs avantages : la prise en charge de l'injection de dépendances, l'accès à la couche modèle de votre application et la gestion centralisée de toutes les extensions. Elles prennent aussi en charge les balises personnalisées, les providers, les passes de compilation et bien d'autres choses.
Configurer tous les templates
Le service TemplateFactory, qui crée tous les templates, expose un tableau public de callbacks
$onCreate. Ils sont appelés à chaque création d'un template : depuis un seul endroit, vous pouvez donc définir
des filtres, des fonctions ou des variables pour tous les templates de l'application. Chaque callback reçoit le template
fraîchement créé. Faites-vous injecter le
service TemplateFactory et enregistrez les callbacks, par exemple au démarrage de l'application :
$templateFactory->onCreate[] = function (Nette\Bridges\ApplicationLatte\Template $template): void {
$template->addFilter('money', fn($val) => '$' . number_format($val, 2));
};
Traduction
Si vous programmez une application multilingue, vous aurez probablement besoin d'afficher certains textes dans le template dans
différentes langues. Nette Framework définit à cet effet une interface pour la traduction Nette\Localization\Translator, qui a une seule
méthode translate(). Elle accepte un message $message, qui est généralement une chaîne, et tout
autre paramètre. La tâche est de retourner la chaîne traduite. Il n'y a pas d'implémentation par défaut dans Nette, vous
pouvez choisir parmi plusieurs solutions prêtes à l'emploi selon vos besoins, que vous trouverez sur Componette. Dans leur documentation, vous apprendrez comment configurer le
traducteur.
Il est possible de définir un traducteur pour les templates, que nous recevrons via injection, avec la méthode
setTranslator() :
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator);
}
Le traducteur peut alternativement être défini via la configuration :
latte:
extensions:
- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)
Ensuite, le traducteur peut être utilisé par exemple comme filtre |translate, y compris avec des paramètres
supplémentaires qui sont passés à la méthode translate() (voir foo, bar) :
<a href="basket">{='Panier'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>
Ou comme balise soulignée :
<a href="basket">{_'Panier'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>
Pour traduire une section du template, il existe une balise paire {translate} (depuis Latte 2.11, auparavant la
balise {_} était utilisée) :
<a href="order">{translate}Commande{/translate}</a>
<a href="order">{translate foo, bar}Commande{/translate}</a>
Le traducteur est appelé par défaut à l'exécution lors du rendu du template. Cependant, Latte version 3 peut traduire tous les textes statiques déjà pendant la compilation du template. Cela économise des performances, car chaque chaîne n'est traduite qu'une seule fois et la traduction résultante est écrite dans la forme compilée. Ainsi, plusieurs versions compilées du template sont créées dans le répertoire cache, une pour chaque langue. Pour cela, il suffit d'indiquer la langue comme deuxième paramètre :
protected function beforeRender(): void
{
// ...
$this->template->setTranslator($translator, $lang);
}
Par texte statique, on entend par exemple {_'hello'} ou {translate}hello{/translate}. Les textes non
statiques, comme par exemple {_$foo}, continueront d'être traduits à l'exécution.