Templates

Nette verwendet das Template-System Latte. Zum einen, weil es das sicherste Template-System für PHP ist, und zum anderen, weil es das intuitivste System ist. Sie müssen nicht viel Neues lernen, PHP-Kenntnisse und ein paar Tags reichen aus.

Es ist üblich, dass eine Seite aus einer Layout-Vorlage und einer Vorlage für die jeweilige Aktion zusammengesetzt wird. So kann zum Beispiel eine Layout-Vorlage aussehen, beachten Sie die {block} Blöcke und den {include} Tag:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}Meine App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

Und das wird die Aktionsvorlage sein:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

Diese definiert den Block content, der anstelle von {include content} im Layout eingefügt wird, und definiert auch den Block title neu, der {block title} im Layout überschreibt. Versuchen Sie, sich das Ergebnis vorzustellen.

Finden von Vorlagen

Sie müssen in den Presentern nicht angeben, welche Vorlage gerendert werden soll; das Framework leitet den Pfad selbst ab und erspart Ihnen das Schreiben.

Wenn Sie eine Verzeichnisstruktur verwenden, in der jeder Presenter sein eigenes Verzeichnis hat, platzieren Sie die Vorlage einfach in diesem Verzeichnis unter dem Namen der Aktion (bzw. der View), d.h. für die Aktion default verwenden Sie die Vorlage default.latte:

app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Wenn Sie eine Struktur verwenden, bei der sich die Presenter gemeinsam in einem Verzeichnis und die Vorlagen im Ordner templates befinden, speichern Sie sie entweder in der Datei <Presenter>.<view>.latte oder <Presenter>/<view>.latte:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1. Variante
        └── Home/
            └── default.latte   ← 2. Variante

Der Ordner templates kann auch eine Ebene höher platziert werden, d.h. auf derselben Ebene wie das Verzeichnis mit den Presenter-Klassen.

Wenn die Vorlage nicht gefunden wird, antwortet der Presenter mit einem Fehler 404 – Seite nicht gefunden.

Die View ändern Sie mit $this->setView('andereView'). Es ist auch möglich, die Datei mit der Vorlage direkt mit $this->template->setFile('/path/to/template.latte') anzugeben.

Die Dateien, in denen nach Vorlagen gesucht wird, können durch Überschreiben der Methode formatTemplateFiles() geändert werden, die ein Array möglicher Dateinamen zurückgibt.

Finden der Layout-Vorlage

Nette sucht auch automatisch nach der Layout-Datei.

Wenn Sie eine Verzeichnisstruktur verwenden, in der jeder Presenter sein eigenes Verzeichnis hat, platzieren Sie das Layout entweder im Ordner des Presenters, wenn es nur für diesen spezifisch ist, oder eine Ebene höher, wenn es für mehrere Presenter gemeinsam genutzt wird:

app/
└── Presentation/
    ├── @layout.latte           ← gemeinsames Layout
    └── Home/
        ├── @layout.latte       ← nur für Presenter Home
        ├── HomePresenter.php
        └── default.latte

Wenn Sie eine Struktur verwenden, bei der sich die Presenter gemeinsam in einem Verzeichnis und die Vorlagen im Ordner templates befinden, wird das Layout an diesen Stellen erwartet:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← gemeinsames Layout
        ├── Home.@layout.latte  ← nur für Home, 1. Variante
        └── Home/
            └── @layout.latte   ← nur für Home, 2. Variante

Wenn sich der Presenter in einem Modul befindet, wird auch auf weiteren Verzeichnisebenen höher gesucht, je nach Verschachtelung des Moduls.

Der Name des Layouts kann mit $this->setLayout('layoutAdmin') geändert werden, dann wird es in der Datei @layoutAdmin.latte erwartet. Es ist auch möglich, die Datei mit der Layout-Vorlage direkt mit $this->setLayout('/path/to/template.latte') anzugeben.

Mit $this->setLayout(false) oder dem Tag {layout none} innerhalb der Vorlage wird die Layout-Suche deaktiviert.

Die Dateien, in denen nach Layout-Vorlagen gesucht wird, können durch Überschreiben der Methode formatLayoutTemplateFiles() geändert werden, die ein Array möglicher Dateinamen zurückgibt.

Variablen in der Vorlage

Variablen werden an die Vorlage übergeben, indem wir sie in $this->template schreiben. Danach stehen sie in der Vorlage als lokale Variablen zur Verfügung:

$this->template->article = $this->articles->getById($id);

So einfach können wir beliebige Variablen an Vorlagen übergeben. Bei der Entwicklung robuster Anwendungen ist es jedoch nützlicher, sich einzuschränken. Zum Beispiel, indem wir explizit eine Liste der Variablen definieren, die die Vorlage erwartet, und deren Typen. Dadurch kann PHP die Typen prüfen, die IDE korrekt Vorschläge machen und die statische Analyse Fehler aufdecken.

Und wie definieren wir eine solche Liste? Einfach in Form einer Klasse und ihrer Eigenschaften. Wir benennen sie ähnlich wie den Presenter, nur mit Template am Ende:

/**
 * @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;

	// und weitere Variablen
}

Das Objekt $this->template im Presenter ist nun eine Instanz der Klasse ArticleTemplate. PHP prüft also beim Schreiben die deklarierten Typen. Ab PHP Version 8.2 wird auch auf das Schreiben in eine nicht existierende Variable hingewiesen, in früheren Versionen kann dasselbe durch die Verwendung des Traits Nette\SmartObject erreicht werden.

Die Annotation @property-read ist für IDEs und statische Analyse gedacht, dank ihr funktioniert die Autovervollständigung, siehe PhpStorm und Code-Vervollständigung für $this->template.

Den Luxus der Autovervollständigung können Sie sich auch in Vorlagen gönnen. Installieren Sie einfach das Latte-Plugin für PhpStorm und geben Sie den Klassennamen am Anfang der Vorlage an, mehr dazu im Artikel Latte: wie man das Typsystem benutzt:

{templateType App\Presentation\Article\ArticleTemplate}
...

So funktionieren auch Vorlagen in Komponenten. Halten Sie einfach die Namenskonvention ein und erstellen Sie für eine Komponente wie FifteenControl eine Vorlagenklasse FifteenTemplate.

Wenn Sie $template als Instanz einer anderen Klasse erstellen müssen, verwenden Sie die Methode createTemplate():

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

Standardvariablen

Presenter und Komponenten übergeben automatisch einige nützliche Variablen an die Vorlagen:

  • $basePath ist der absolute URL-Pfad zum Stammverzeichnis (z.B. /eshop)
  • $baseUrl ist die absolute URL zum Stammverzeichnis (z.B. http://localhost/eshop)
  • $user ist das Objekt, das den Benutzer repräsentiert
  • $presenter ist der aktuelle Presenter
  • $control ist die aktuelle Komponente oder der aktuelle Presenter
  • $flashes Array von Nachrichten, die mit der Funktion flashMessage() gesendet wurden

Wenn Sie Ihre eigene Vorlagenklasse verwenden, werden diese Variablen übergeben, wenn Sie eine Eigenschaft für sie erstellen.

In der Vorlage werden Links zu anderen Presentern & Aktionen auf diese Weise erstellt:

<a n:href="Product:show">Produktdetail</a>

Das Attribut n:href ist sehr praktisch für HTML-Tags <a>. Wenn wir den Link an anderer Stelle ausgeben möchten, zum Beispiel im Text, verwenden wir {link}:

Die Adresse ist: {link Home:default}

Weitere Informationen finden Sie im Kapitel Erstellen von URL-Links.

Eigene Filter, Tags usw.

Das Latte-Template-System kann um eigene Filter, Funktionen, Tags usw. erweitert werden. Dies kann direkt in der Methode render<View> oder beforeRender() geschehen:

public function beforeRender(): void
{
	// Hinzufügen eines Filters
	$this->template->addFilter('foo', /* ... */);

	// oder wir konfigurieren direkt das Latte\Engine Objekt
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latte in Version 3 bietet einen fortgeschritteneren Weg, nämlich die Erstellung einer Extension für jedes Webprojekt. Ein kurzes Beispiel einer solchen Klasse:

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()),
			// ...
		];
	}

	// ...
}

Wir registrieren sie über die Konfiguration:

latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension

Übersetzung

Wenn Sie eine mehrsprachige Anwendung programmieren, müssen Sie wahrscheinlich einige Texte in der Vorlage in verschiedenen Sprachen ausgeben. Zu diesem Zweck definiert das Nette Framework ein Übersetzungs-Interface Nette\Localization\Translator, das nur eine Methode translate() hat. Diese nimmt die Nachricht $message entgegen, die normalerweise eine Zeichenkette ist, sowie beliebige weitere Parameter. Die Aufgabe besteht darin, die übersetzte Zeichenkette zurückzugeben. In Nette gibt es keine Standardimplementierung; Sie können je nach Bedarf aus mehreren fertigen Lösungen wählen, die Sie auf Componette finden. In deren Dokumentation erfahren Sie, wie Sie den Translator konfigurieren.

Für Vorlagen kann ein Translator, den wir uns übergeben lassen, mit der Methode setTranslator() festgelegt werden:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

Alternativ kann der Translator über die Konfiguration eingestellt werden:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

Danach kann der Translator beispielsweise als Filter |translate verwendet werden, einschließlich zusätzlicher Parameter, die an die Methode translate() übergeben werden (siehe foo, bar):

<a href="basket">{='Warenkorb'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

Oder als Unterstrich-Tag:

<a href="basket">{_'Warenkorb'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

Für die Übersetzung eines Vorlagenabschnitts gibt es den paarweisen Tag {translate} (seit Latte 2.11, früher wurde der Tag {_} verwendet):

<a href="order">{translate}Bestellung{/translate}</a>
<a href="order">{translate foo, bar}Bestellung{/translate}</a>

Der Translator wird standardmäßig zur Laufzeit beim Rendern der Vorlage aufgerufen. Latte Version 3 kann jedoch alle statischen Texte bereits während der Kompilierung der Vorlage übersetzen. Dadurch wird Leistung gespart, da jede Zeichenkette nur einmal übersetzt wird und die resultierende Übersetzung in die kompilierte Form geschrieben wird. Im Cache-Verzeichnis entstehen so mehrere kompilierte Versionen der Vorlage, eine für jede Sprache. Dazu genügt es, die Sprache als zweiten Parameter anzugeben:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

Mit statischem Text ist z.B. {_'hello'} oder {translate}hello{/translate} gemeint. Nicht-statische Texte, wie z.B. {_$foo}, werden weiterhin zur Laufzeit übersetzt.

Version: 4.0