Loader

Loader sind der Mechanismus, mit dem Latte den Quellcode Ihrer Templates beschafft. Meist sind Templates Dateien auf der Festplatte, aber das flexible Loader-System von Latte erlaubt es, sie praktisch von überall zu laden oder sogar dynamisch zu erzeugen.

Was ist ein Loader?

Wenn Sie mit Templates arbeiten, denken Sie üblicherweise an .latte-Dateien in der Verzeichnisstruktur Ihres Projekts. Darum kümmert sich der standardmäßige FileLoader von Latte. Die Verbindung zwischen dem Namen eines Templates (etwa 'main.latte' oder 'components/card.latte') und dem eigentlichen Quellcode muss aber keine direkte Abbildung auf einen Dateipfad sein.

Genau hier kommen Loader ins Spiel. Ein Loader ist ein Objekt, das den Namen eines Templates (einen Bezeichner-String) entgegennimmt und Latte dessen Quellcode liefert. Latte verlässt sich bei dieser Aufgabe vollständig auf den konfigurierten Loader. Das gilt nicht nur für das zuerst angeforderte Template über $latte->render('main.latte'), sondern auch für jedes darin referenzierte Template, das mit Tags wie {include ...}, {layout ...}, {embed ...} oder {import ...} eingebunden wird.

Wozu ein eigener Loader?

  • Laden aus anderen Quellen: Templates aus einer Datenbank holen, aus einem Cache (etwa Redis oder Memcached), aus einer Versionsverwaltung (etwa Git, anhand eines bestimmten Commits) oder dynamisch erzeugen.
  • Eigene Namenskonventionen umsetzen: Sie wollen vielleicht kürzere Aliase für Templates verwenden oder eine eigene Suchlogik für Pfade umsetzen (z. B. zuerst im Verzeichnis des Themes suchen und dann auf ein Standardverzeichnis zurückfallen).
  • Sicherheit oder Zugriffskontrolle ergänzen: Ein eigener Loader könnte vor dem Laden bestimmter Templates die Berechtigungen des Benutzers prüfen.
  • Vorverarbeitung: Auch wenn davon generell abzuraten ist (Compiler-Pässe sind besser), könnte ein Loader den Inhalt eines Templates theoretisch vorverarbeiten, bevor er ihn an Latte übergibt.

Den Loader setzen Sie für eine Instanz von Latte\Engine mit der Methode setLoader():

$latte = new Latte\Engine;

// Verwendung des standardmäßigen FileLoader für Dateien in '/path/to/templates'
$loader = new Latte\Loaders\FileLoader('/path/to/templates');
$latte->setLoader($loader);

Ein Loader muss das Interface Latte\Loader implementieren.

Eingebaute Loader

Latte bietet mehrere Standard-Loader:

FileLoader

Das ist der standardmäßige Loader, den die Klasse Latte\Engine verwendet, wenn kein anderer angegeben ist. Er lädt Templates direkt aus dem Dateisystem.

Optional können Sie ein Wurzelverzeichnis setzen, um den Zugriff einzuschränken:

use Latte\Loaders\FileLoader;

// Das Folgende erlaubt nur das Laden von Templates aus dem Verzeichnis /var/www/html/templates
$loader = new FileLoader('/var/www/html/templates');
$latte->setLoader($loader);

// $latte->render('../../../etc/passwd'); // Das würde eine Exception werfen

// Rendern eines Templates unter /var/www/html/templates/pages/contact.latte
$latte->render('pages/contact.latte');

Bei Tags wie {include} oder {layout} löst er Template-Namen relativ zum aktuellen Template auf, sofern kein absoluter Pfad angegeben ist. Ist jedoch ein Wurzelverzeichnis gesetzt, werden alle Namen relativ zum aktuellen Template aufgelöst.

StringLoader

Dieser Loader holt den Inhalt der Templates aus einem assoziativen Array, in dem die Schlüssel die Namen der Templates (Bezeichner) und die Werte die Strings mit dem Quellcode sind. Besonders nützlich ist er zum Testen oder in kleinen Anwendungen, in denen Templates direkt im PHP-Code liegen können.

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}',
	// nach Bedarf weitere Templates ergänzen
]);

$latte->setLoader($loader);

$latte->render('main.latte', ['name' => 'World']);
// Ausgabe: Hello World, include is below:Included content: 10

Wenn Sie nur ein einziges Template direkt aus einem String rendern müssen, ohne includes oder Vererbung mit Bezug auf andere benannte String-Templates, können Sie den String bei Verwendung des StringLoader ohne Array direkt an die Methode render() oder renderToString() übergeben:

$loader = new StringLoader;
$latte->setLoader($loader);

$templateString = 'Hello {$name}!';
$output = $latte->renderToString($templateString, ['name' => 'Alice']);
// $output enthält 'Hello Alice!'

Einen eigenen Loader erstellen

Um einen eigenen Loader zu erstellen (etwa zum Laden von Templates aus einer Datenbank, einem Cache, einer Versionsverwaltung oder einer anderen Quelle), müssen Sie eine Klasse anlegen, die das Interface Latte\Loader implementiert.

Sehen wir uns an, was jede Methode leisten muss.

getContent (string $name)string

Das ist die zentrale Methode des Loaders. Ihre Aufgabe ist es, den vollständigen Quellcode des durch $name bezeichneten Templates zu beschaffen und zurückzugeben (so, wie er an die Methode $latte->render() übergeben oder von der Methode getReferredName() zurückgegeben wurde).

Lässt sich das Template nicht finden oder darauf nicht zugreifen, muss diese Methode eine Latte\TemplateNotFoundException werfen.

public function getContent(string $name): string
{
	// Beispiel: Laden aus einem hypothetischen internen Speicher
	$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

Diese Methode kümmert sich um die Auflösung der Template-Namen, die in Tags wie {include}, {layout} usw. verwendet werden. Trifft Latte zum Beispiel in main.latte auf {include 'partial.latte'}, ruft es diese Methode mit $name = 'partial.latte' und $referringName = 'main.latte' auf.

Aufgabe der Methode ist es, $name anhand des durch $referringName gegebenen Kontexts in einen kanonischen Bezeichner aufzulösen (z. B. einen absoluten Pfad, einen eindeutigen Datenbankschlüssel), der beim Aufruf der übrigen Methoden des Loaders verwendet wird.

public function getReferredName(string $name, string $referringName): string
{
	return ...;
}

getUniqueId (string $name)string

Latte verwendet zur Verbesserung der Leistung einen Cache kompilierter Templates. Jede Datei mit einem kompilierten Template braucht einen eindeutigen Namen, der vom Bezeichner des Quell-Templates abgeleitet ist. Diese Methode liefert einen String, der das Template $name eindeutig identifiziert.

Bei dateibasierten Templates kann dazu der absolute Pfad dienen. Bei Templates in einer Datenbank ist eine Kombination aus Präfix und Datenbank-ID üblich.

public function getUniqueId(string $name): string
{
	return ...;
}

Beispiel: Einfacher Datenbank-Loader

Dieses Beispiel zeigt den grundlegenden Aufbau eines Loaders, der Templates aus einer Datenbanktabelle namens templates mit den Spalten name (eindeutiger Bezeichner), content und updated_at lädt.

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

	// Dieses einfache Beispiel setzt voraus, dass die Namen der Templates ('homepage', 'article' usw.)
	// eindeutige IDs sind und die Templates einander nicht relativ referenzieren.
	public function getReferredName(string $name, string $referringName): string
	{
		return $name;
	}

	public function getUniqueId(string $name): string
	{
		// Ein Präfix zusammen mit dem Namen selbst ist hier eindeutig und ausreichend
		return 'db_' . $name;
	}
}

// Verwendung:
$pdo = new \PDO(/* Verbindungsdaten */);
$loader = new DatabaseLoader($pdo);
$latte->setLoader($loader);
$latte->render('homepage'); // Lädt das Template namens 'homepage' aus der DB

Eigene Loader geben Ihnen vollständige Kontrolle darüber, woher Ihre Latte-Templates kommen, und ermöglichen die Anbindung an verschiedene Speichersysteme und Arbeitsabläufe.

Version: 3.x