Loaders
Les loaders sont le mécanisme par lequel Latte obtient le code source de vos templates. Le plus souvent, les templates sont des fichiers stockés sur le disque, mais le système souple de loaders de Latte vous permet de les charger depuis pratiquement n'importe où, voire de les générer dynamiquement.
Qu'est-ce qu'un loader ?
Quand vous travaillez avec des templates, vous pensez généralement à des fichiers .latte situés dans
l'arborescence de votre projet. C'est ce dont s'occupe le FileLoader par défaut de Latte. Le lien
entre le nom d'un template (comme 'main.latte' ou 'components/card.latte') et son code source réel n'a
toutefois pas à être une simple correspondance avec un chemin de fichier.
C'est là qu'interviennent les loaders. Un loader est un objet chargé de prendre un nom de template (une chaîne servant
d'identifiant) et de fournir à Latte son code source. Latte s'en remet entièrement au loader configuré pour cette tâche. Cela
ne vaut pas seulement pour le template initial demandé via $latte->render('main.latte'), mais aussi pour
chaque template référencé à l'intérieur par des balises comme {include ...}, {layout ...},
{embed ...} ou {import ...}.
Pourquoi utiliser un loader personnalisé ?
- Chargement depuis des sources alternatives : récupérer des templates stockés dans une base de données, dans un cache (Redis, Memcached), dans un système de gestion de versions (Git, à partir d'un commit précis) ou générés dynamiquement.
- Conventions de nommage personnalisées : vous pouvez vouloir des alias plus courts pour les templates, ou une logique de recherche particulière (chercher d'abord dans le répertoire du thème, puis se rabattre sur le répertoire par défaut).
- Sécurité et contrôle d'accès : un loader personnalisé peut vérifier les permissions de l'utilisateur avant de charger certains templates.
- Prétraitement : bien que généralement déconseillé (les passes de compilation conviennent mieux), un loader pourrait en théorie prétraiter le contenu du template avant de le remettre à Latte.
Vous définissez le loader d'une instance de Latte\Engine avec la méthode setLoader() :
$latte = new Latte\Engine;
// Utilisation du FileLoader par défaut pour les fichiers de '/path/to/templates'
$loader = new Latte\Loaders\FileLoader('/path/to/templates');
$latte->setLoader($loader);
Un loader doit implémenter l'interface Latte\Loader.
Loaders intégrés
Latte propose plusieurs loaders standard :
FileLoader
C'est le loader par défaut qu'utilise la classe Latte\Engine si aucun autre n'est indiqué. Il charge les
templates directement depuis le système de fichiers.
Vous pouvez éventuellement définir un répertoire racine pour restreindre l'accès :
use Latte\Loaders\FileLoader;
// Ce qui suit n'autorisera le chargement de templates que depuis le répertoire /var/www/html/templates
$loader = new FileLoader('/var/www/html/templates');
$latte->setLoader($loader);
// $latte->render('../../../etc/passwd'); // Ceci lèverait une exception
// Rendu d'un template situé dans /var/www/html/templates/pages/contact.latte
$latte->render('pages/contact.latte');
Avec des balises comme {include} ou {layout}, il résout les noms de templates relativement au
template courant, sauf si un chemin absolu est indiqué. Si un répertoire racine est défini, en revanche, tous les noms sont
résolus relativement au template courant.
StringLoader
Ce loader récupère le contenu des templates dans un tableau associatif dont les clés sont les noms des templates (leurs identifiants) et les valeurs les chaînes contenant leur code source. Il est particulièrement utile pour les tests ou les petites applications, où les templates peuvent être stockés directement dans le code PHP.
use Latte\Loaders\StringLoader;
$loader = new StringLoader([
'main.latte' => 'Hello {$name}, include is below:{include helper.latte}',
'helper.latte' => '{var $x = 10}Included content: {$x}',
// Ajoutez d'autres templates selon les besoins
]);
$latte->setLoader($loader);
$latte->render('main.latte', ['name' => 'World']);
// Sortie : Hello World, include is below:Included content: 10
Si vous n'avez besoin de rendre qu'un seul template directement depuis une chaîne, sans inclusion ni héritage renvoyant à
d'autres templates nommés, vous pouvez passer la chaîne directement à la méthode render() ou
renderToString() en utilisant StringLoader sans tableau :
$loader = new StringLoader;
$latte->setLoader($loader);
$templateString = 'Hello {$name}!';
$output = $latte->renderToString($templateString, ['name' => 'Alice']);
// $output contient 'Hello Alice!'
Création d'un loader personnalisé
Pour créer votre propre loader (par exemple pour charger des templates depuis une base de données, un cache, un système de gestion de versions ou une autre source), vous devez écrire une classe qui implémente l'interface Latte\Loader.
Voyons ce que chaque méthode doit faire.
getContent (string $name): string
C'est la méthode centrale du loader. Sa tâche est de récupérer et de retourner le code source complet du template
identifié par $name (tel qu'il a été passé à la méthode $latte->render() ou retourné par la
méthode getReferredName()).
Si le template est introuvable ou inaccessible, cette méthode doit lever une exception
Latte\TemplateNotFoundException.
public function getContent(string $name): string
{
// Exemple : chargement depuis un stockage interne hypothétique
$content = $this->storage->read($name);
if ($content === null) {
throw new Latte\TemplateNotFoundException("Template '$name' cannot be loaded.");
}
return $content;
}
getReferredName (string $name, string $referringName): string
Cette méthode gère la résolution des noms de templates utilisés dans les balises comme {include},
{layout}, etc. Lorsque Latte rencontre par exemple {include 'partial.latte'} à l'intérieur de
main.latte, il appelle cette méthode avec $name = 'partial.latte' et
$referringName = 'main.latte'.
Le travail de la méthode consiste à résoudre $name en un identifiant canonique (chemin absolu, clé unique en
base de données…) qui servira lors des appels aux autres méthodes du loader, en s'appuyant sur le contexte fourni par
$referringName.
public function getReferredName(string $name, string $referringName): string
{
return ...;
}
getUniqueId (string $name): string
Latte utilise un cache de templates compilés pour améliorer les performances. Chaque fichier de template compilé a besoin
d'un nom unique dérivé de l'identifiant du template source. Cette méthode fournit une chaîne qui identifie de façon
unique le template $name.
Pour les templates basés sur des fichiers, le chemin absolu peut suffire. Pour les templates en base de données, on utilise couramment la combinaison d'un préfixe et de l'ID en base.
public function getUniqueId(string $name): string
{
return ...;
}
Exemple : un loader de base de données simple
Cet exemple montre la structure de base d'un loader qui charge des templates stockés dans une table nommée
templates, avec les colonnes name (identifiant unique), content et
updated_at.
use Latte;
class DatabaseLoader implements Latte\Loader
{
public function __construct(
private \PDO $db,
) {
}
public function getContent(string $name): string
{
$stmt = $this->db->prepare('SELECT content FROM templates WHERE name = ?');
$stmt->execute([$name]);
$content = $stmt->fetchColumn();
if ($content === false) {
throw new Latte\TemplateNotFoundException("Template '$name' not found in database.");
}
return $content;
}
// Cet exemple simple suppose que les noms de templates ('homepage', 'article', etc.)
// sont des ID uniques et que les templates ne se référencent pas relativement.
public function getReferredName(string $name, string $referringName): string
{
return $name;
}
public function getUniqueId(string $name): string
{
// Un préfixe suivi du nom lui-même est unique et suffisant ici
return 'db_' . $name;
}
}
// Utilisation :
$pdo = new \PDO(/* détails de connexion */);
$loader = new DatabaseLoader($pdo);
$latte->setLoader($loader);
$latte->render('homepage'); // Charge le template nommé 'homepage' depuis la BDD
Les loaders personnalisés vous donnent un contrôle total sur la provenance de vos templates Latte et permettent l'intégration avec toutes sortes de systèmes de stockage et de flux de travail.