Eigene Filter erstellen

Filter sind mächtige Werkzeuge, um Daten direkt in Latte-Templates zu formatieren und zu verändern. Sie bieten eine saubere Syntax mit dem Pipe-Symbol (|), um Variablen oder Ergebnisse von Ausdrücken in das gewünschte Ausgabeformat zu bringen.

Was sind Filter?

Filter sind in Latte im Grunde PHP-Funktionen, die dafür gedacht sind, einen Eingabewert in einen Ausgabewert umzuwandeln. Sie werden in Template-Ausdrücken ({...}) mit der Pipe-Schreibweise (|) angewendet.

Bequemlichkeit: Filter erlauben es, gängige Formatierungsaufgaben (etwa Datumsformatierung, Umwandlung der Groß-/Kleinschreibung, Kürzen) oder Datenmanipulationen in wiederverwendbare Einheiten zu kapseln. Statt komplexen PHP-Code in Ihren Templates zu wiederholen, wenden Sie einfach einen Filter an:

{* Statt komplexem PHP zum Kürzen: *}
{$article->text|truncate:100}

{* Statt Code zur Datumsformatierung: *}
{$event->startTime|date:'Y-m-d H:i'}

{* Mehrere Umwandlungen anwenden: *}
{$product->name|lower|capitalize}

Lesbarkeit: Der Einsatz von Filtern macht Templates sauberer und stärker auf die Präsentation ausgerichtet, weil die Umwandlungslogik in die Definition des Filters wandert.

Kontextabhängigkeit: Eine wesentliche Stärke der Latte-Filter ist, dass sie kontextabhängig sein können. Ein Filter kann also erkennen, mit welcher Art von Inhalt er arbeitet (HTML, JavaScript, reiner Text usw.), und entsprechende Logik oder Escaping anwenden. Das ist für Sicherheit und Korrektheit entscheidend, vor allem beim Erzeugen von HTML.

Verzahnung mit der Anwendungslogik: Genau wie bei eigenen Funktionen kann das PHP-Callable hinter einem Filter eine Closure, eine statische Methode oder eine Instanzmethode sein. Filter können damit bei Bedarf auf Services oder Daten der Anwendung zugreifen, auch wenn ihr Hauptzweck weiterhin die Umwandlung des Eingabewerts bleibt.

Standardmäßig bietet Latte einen reichen Satz an Standardfiltern. Eigene Filter erlauben es Ihnen, diesen Satz um die Formatierungen und Umwandlungen zu erweitern, die Ihr Projekt braucht.

Wenn Sie Logik brauchen, die auf mehreren Eingaben beruht, oder keinen primären Wert zum Umwandeln haben, passt vermutlich eher eine eigene Funktion. Wenn Sie komplexes Markup erzeugen oder den Ablauf des Templates steuern wollen, ziehen Sie einen eigenen Tag in Betracht.

Filter erstellen und registrieren

Es gibt mehrere Wege, eigene Filter in Latte zu definieren und zu registrieren.

Direkte Registrierung über addFilter()

Der einfachste Weg, einen Filter hinzuzufügen, ist die Methode addFilter() direkt am Objekt Latte\Engine. Sie geben den Namen des Filters an (so, wie er im Template verwendet wird) und das zugehörige PHP-Callable.

$latte = new Latte\Engine;

// Einfacher Filter ohne Argumente
$latte->addFilter('initial', fn(string $s): string => mb_substr($s, 0, 1) . '.');

// Filter mit einem optionalen Argument
$latte->addFilter('shortify', function (string $s, int $len = 10): string {
	return mb_substr($s, 0, $len);
});

// Filter, der ein Array verarbeitet
$latte->addFilter('sum', fn(array $numbers): int|float => array_sum($numbers));

Verwendung im Template:

{$name|initial}                 {* gibt 'J.' aus, wenn $name 'John' ist *}
{$description|shortify}         {* verwendet die Standardlänge 10 *}
{$description|shortify:50}      {* verwendet die Länge 50 *}
{$prices|sum}                   {* gibt die Summe der Elemente im Array $prices aus *}

Übergabe der Argumente:

Der Wert links von der Pipe (|) wird der Filterfunktion immer als erstes Argument übergeben. Alle im Template nach dem Doppelpunkt (:) angegebenen Parameter werden als weitere Argumente übergeben.

{$text|shortify:30}
// Ruft die PHP-Funktion shortify($text, 30) auf

Registrierung über eine Extension

Für bessere Organisation, besonders beim Erstellen wiederverwendbarer Filtersätze oder beim Teilen als Paket, ist der empfohlene Weg die Registrierung innerhalb einer Latte-Extension:

namespace App\Templating;

use Latte\Extension;

class MyLatteExtension extends Extension
{
	public function getFilters(): array
	{
		return [
			'initial' => $this->initial(...),
			'shortify' => $this->shortify(...),
		];
	}

	public function initial(string $s): string
	{
		return mb_substr($s, 0, 1) . '.';
	}

	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}
}

// Registrierung
$latte = new Latte\Engine;
$latte->addExtension(new MyLatteExtension);

Dieser Ansatz hält die Logik Ihrer Filter gekapselt und macht die Registrierung unkompliziert.

Filter über eine Klasse mit Attributen

Ein weiterer eleganter Weg, Filter zu definieren, sind Methoden in Ihrer Klasse mit Template-Parametern. Ergänzen Sie die Methode einfach um das Attribut #[Latte\Attributes\TemplateFilter].

use Latte\Attributes\TemplateFilter;

class TemplateParameters
{
	public function __construct(
		public string $description,
		// weitere Parameter...
	) {}

	#[TemplateFilter]
	public function shortify(string $s, int $len = 10): string
	{
		return mb_substr($s, 0, $len);
	}
}

// Das Objekt an das Template übergeben
$params = new TemplateParameters(description: '...');
$latte->render('template.latte', $params);

Latte erkennt und registriert die mit diesem Attribut gekennzeichneten Methoden automatisch, sobald das Objekt TemplateParameters an das Template übergeben wird. Der Name des Filters im Template entspricht dem Methodennamen (hier also shortify).

{* Den in der Parameterklasse definierten Filter verwenden *}
{$description|shortify:50}

Kontextabhängige Filter

Manchmal braucht ein Filter mehr Informationen als nur den Eingabewert. Er muss vielleicht den Inhaltstyp des Strings kennen, den er verarbeitet (z. B. HTML, JavaScript, reiner Text), oder ihn sogar ändern. Genau dafür gibt es kontextabhängige Filter.

Ein kontextabhängiger Filter wird genauso definiert wie ein gewöhnlicher, sein erster Parameter muss jedoch mit dem Typ Latte\Runtime\FilterInfo deklariert sein. Latte erkennt diese Signatur automatisch und übergibt beim Aufruf des Filters das Objekt FilterInfo. Die weiteren Parameter erhalten wie gewohnt die Argumente des Filters.

use Latte\Runtime\FilterInfo;
use Latte\ContentType;

$latte->addFilter('money', function (FilterInfo $info, float $amount): string {
	// 1. Den Inhaltstyp der Eingabe prüfen (optional, aber empfohlen)
	//    null (Eingabe aus einer Variablen) oder reinen Text zulassen. Bei HTML usw. ablehnen.
	if (!in_array($info->contentType, [null, ContentType::Text], true)) {
		$actualType = $info->contentType ?? 'mixed';
		throw new \RuntimeException(
			"Filter |money used in incompatible content type $actualType. Expected text or null."
		);
	}

	// 2. Die Umwandlung durchführen
	$formatted = number_format($amount, 2, '.', ',') . ' EUR';
	$htmlOutput = '<i>' . htmlspecialchars($formatted) . '</i>'; // Für korrektes Escaping sorgen!

	// 3. Den Inhaltstyp der Ausgabe deklarieren
	$info->contentType = ContentType::Html;

	// 4. Das Ergebnis zurückgeben
	return $htmlOutput;
});

$info->contentType ist eine String-Konstante aus Latte\ContentType (z. B. ContentType::Html, ContentType::Text, ContentType::JavaScript usw.) oder null, wenn der Filter auf eine Variable angewendet wird ({$var|filter}). Sie können ihn lesen, um den Kontext der Eingabe zu prüfen, und schreiben, um den Typ des Ausgabekontexts zu deklarieren.

Indem Sie den Inhaltstyp auf HTML setzen, teilen Sie Latte mit, dass der von Ihrem Filter zurückgegebene String sicheres HTML ist. Latte wendet auf dieses Ergebnis dann kein automatisches Escaping mehr an. Das ist entscheidend, wenn Ihr Filter HTML-Markup erzeugt.

Wenn Ihr Filter HTML erzeugt, sind Sie für das korrekte Escaping aller darin verwendeten Eingabedaten verantwortlich (wie im obigen Aufruf htmlspecialchars($formatted)). Versäumen Sie das, können XSS-Sicherheitslücken entstehen. Gibt Ihr Filter nur reinen Text zurück, müssen Sie $info->contentType nicht setzen.

Filter auf Blöcken

Filter, die auf Blöcke mit einem anderen Inhaltstyp als Text (typischerweise HTML) angewendet werden, müssen kontextabhängig sein. Der Grund ist, dass der Inhalt des Blocks einen definierten Inhaltstyp hat, den der Filter kennen muss. Ein klassischer, nicht kontextabhängiger Filter lässt sich nur auf einen Block anwenden, dessen Inhalt reiner Text ist.

{block heading|money}1000{/block}
{* Der Filter 'money' erhält '1000' als zweites Argument
   und $info->contentType ist ContentType::Html *}

Kontextabhängige Filter geben Ihnen mächtige Kontrolle darüber, wie Daten anhand ihres Kontexts verarbeitet werden. Sie ermöglichen fortgeschrittene Funktionen und sorgen für korrektes Escaping, vor allem beim Erzeugen von HTML-Inhalten.

Version: 3.x