Presenter
Wir sehen uns an, wie in Nette Presenter und Templates geschrieben werden. Nach der Lektüre werden Sie wissen:
- wie Presenter funktionieren
- was persistente Parameter sind
- wie Templates gerendert werden
Wir wissen bereits, dass ein Presenter eine Klasse ist, die eine konkrete Seite einer Webanwendung repräsentiert, etwa die Startseite, ein Produkt im E-Shop, ein Anmeldeformular, einen Sitemap-Feed usw. Eine Anwendung kann von einem bis zu Tausenden von Presentern haben. In anderen Frameworks sind sie auch als Controller bekannt.
Üblicherweise meint der Begriff Presenter einen Nachfahren der Klasse Nette\Application\UI\Presenter, die sich zum Erzeugen von Weboberflächen eignet und der sich der Rest dieses Kapitels widmet. Im allgemeinen Sinn ist ein Presenter jedes Objekt, das das Interface Nette\Application\IPresenter implementiert.
Lebenszyklus des Presenters
Die Aufgabe des Presenters ist es, einen Request zu bearbeiten und eine Antwort zurückzugeben (das kann eine HTML-Seite sein, ein Bild, eine Weiterleitung usw.).
Zu Beginn wird ihm also ein Request übergeben. Das ist nicht direkt der HTTP-Request, sondern ein Objekt Nette\Application\Request, in das der HTTP-Request mithilfe des Routers umgewandelt wurde. Mit diesem Objekt kommen wir üblicherweise nicht direkt in Berührung, denn der Presenter delegiert die Bearbeitung des Requests geschickt an andere Methoden, die wir uns nun ansehen.
Das Diagramm zeigt eine Liste von Methoden, die der Reihe nach von oben nach unten aufgerufen werden, sofern sie existieren. Keine davon ist verpflichtend; Sie können einen völlig leeren Presenter ohne eine einzige Methode haben und darauf eine einfache statische Website aufbauen.
__construct()
Der Konstruktor gehört streng genommen nicht zum Lebenszyklus des Presenters, da er im Moment der Objekterzeugung aufgerufen wird. Wir erwähnen ihn jedoch wegen seiner Bedeutung. Der Konstruktor (zusammen mit der Inject-Methode) dient der Übergabe von Abhängigkeiten.
Der Presenter sollte nicht die Geschäftslogik der Anwendung erledigen, nicht in die Datenbank schreiben oder aus ihr lesen,
keine Berechnungen anstellen usw. Dafür sind Klassen in der Schicht zuständig, die wir Model nennen. Zum Beispiel kann eine
Klasse ArticleRepository für das Laden und Speichern von Artikeln verantwortlich sein. Damit der Presenter mit ihr
arbeiten kann, muss sie ihm per Dependency Injection
übergeben werden:
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articles,
) {
}
}
startup()
Unmittelbar nach dem Empfang des Requests wird die Methode startup() aufgerufen. Sie können sie zum
Initialisieren von Properties, zum Prüfen von Benutzerberechtigungen usw. verwenden. Es ist erforderlich, dass diese Methode
immer ihren Vorfahren aufruft: parent::startup().
action<Action>(args...)
Ähnlich wie die Methode render<View>(). Während render<View>() dazu gedacht ist, Daten
für ein konkretes Template vorzubereiten, das anschließend gerendert wird, verarbeitet action<Action>() einen
Request, ohne dass danach zwingend ein Template gerendert wird. Sie kann zum Beispiel Daten verarbeiten, einen Benutzer an- oder
abmelden und so weiter, und dann woandershin weiterleiten.
Wichtig ist, dass action<Action>() vor render<View>() aufgerufen wird. Dadurch
können wir den Verlauf des Requests in der Action-Methode noch ändern, etwa das Template, das gerendert wird, oder sogar die
aufzurufende Methode render<View>() mit setView('otherView') austauschen.
Sie können mit der Methode switch('otherAction') sogar zu einer völlig anderen Aktion
wechseln. Sie bricht die aktuelle Methode ab und führt stattdessen die Methoden action<Action>() und
render<View>() der neuen Aktion aus (und schaltet die automatische Kanonisierung ab). Der Request selbst läuft weiter; unterbrochen wird nur die gerade laufende
Methode.
Der Methode werden die Parameter aus dem Request übergeben. Es ist möglich und empfehlenswert, für diese Parameter Typen
anzugeben, z. B. actionShow(int $id, ?string $slug = null). Fehlt der Parameter id oder ist er keine
ganze Zahl, gibt der Presenter einen Fehler 404 zurück und endet.
handle<Signal>(args...)
Diese Methode verarbeitet die sogenannten Signale, die wir im Kapitel über Komponenten kennenlernen. Sie ist vor allem für Komponenten und die Bearbeitung von AJAX-Requests gedacht.
Der Methode werden, genau wie bei action<Action>(), die Parameter aus dem Request übergeben, samt
Typprüfung.
beforeRender()
Die Methode beforeRender wird, wie ihr Name nahelegt, vor jeder Methode render<View>()
aufgerufen. Sie dient der gemeinsamen Konfiguration von Templates, der Übergabe von Variablen an das Layout und Ähnlichem.
render<View>(args...)
Hier bereiten wir das Template für das anschließende Rendern vor, übergeben ihm Daten usw.
Der Methode werden, genau wie bei action<Action>(), die Parameter aus dem Request übergeben, samt
Typprüfung.
public function renderShow(int $id): void
{
// Daten aus dem Model holen und an das Template übergeben
$this->template->article = $this->articles->getById($id);
}
afterRender()
Die Methode afterRender wird, wie der Name wiederum nahelegt, nach jeder Methode render<View>()
aufgerufen. Sie wird eher selten verwendet.
shutdown()
Wird am Ende des Lebenszyklus des Presenters aufgerufen.
Events
Neben den Methoden startup(), beforeRender() und shutdown(), die im Rahmen des
Lebenszyklus des Presenters aufgerufen werden, lassen sich weitere Funktionen definieren, die automatisch aufgerufen werden. Der
Presenter definiert sogenannte Events, und Sie fügen deren Handler in
die Arrays $onStartup, $onRender und $onShutdown ein.
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct()
{
$this->onStartup[] = function () {
// ...
};
}
}
Die Handler im Array $onStartup werden unmittelbar vor der Methode startup() aufgerufen, die Handler
in $onRender zwischen beforeRender() und render<View>() und schließlich die Handler
in $onShutdown unmittelbar vor shutdown().
Ein Rat, bevor wir weitermachen: Wie Sie sehen, kann ein Presenter mehrere Aktionen/Views bearbeiten, also mehrere
Methoden render<View>() haben. Wir empfehlen jedoch, Presenter mit einer oder möglichst wenigen Aktionen zu
entwerfen.
Eine Antwort senden
Die Antwort des Presenters ist typischerweise das Rendern eines Templates in eine HTML-Seite, es kann aber auch das Senden einer Datei sein, von JSON oder sogar eine Weiterleitung auf eine andere Seite.
Zu jedem Zeitpunkt des Lebenszyklus können wir mit einer der folgenden Methoden eine Antwort senden und den Presenter zugleich beenden:
redirect(),redirectPermanent(),redirectUrl()undforward()führen eine Weiterleitung auserror()beendet den Presenter wegen eines FehlerssendJson($data)beendet den Presenter und sendet Daten im JSON-FormatsendTemplate()beendet den Presenter und rendert sofort das TemplatesendResponse($response)beendet den Presenter und sendet eine eigene Antwortterminate()beendet den Presenter ohne Antwort
Jede dieser Methoden beendet den Presenter sofort, indem sie die stille Beendigungs-Exception
Nette\Application\AbortException wirft.
Rufen Sie keine dieser Methoden auf, geht der Presenter automatisch zum Rendern des Templates über. Warum? Weil wir in 99 % der Fälle ein Template rendern wollen, macht der Presenter dieses Verhalten zum Standardverhalten und erleichtert uns die Arbeit.
Links erstellen
Der Presenter hat die Methode link(), mit der URL-Links auf andere Presenter erstellt werden. Der erste Parameter
ist der Ziel-Presenter & die Aktion, gefolgt von Argumenten, die sich auch als Array übergeben lassen:
$url = $this->link('Product:show', $id);
$url = $this->link('Product:show', [$id, 'lang' => 'en']);
Im Template werden Links auf andere Presenter & Aktionen so erstellt:
<a n:href="Product:show $id">Produktdetail</a>
Schreiben Sie einfach das vertraute Paar Presenter:action statt der tatsächlichen URL und ergänzen Sie
eventuelle Parameter. Der Trick steckt in n:href, das Latte sagt, dieses Attribut zu verarbeiten und die echte URL zu
erzeugen. In Nette müssen Sie überhaupt nicht über URLs nachdenken, sondern nur über Presenter und Aktionen.
Mehr dazu finden Sie im Kapitel Erstellen von URL-Links.
Weiterleitung
Zum Wechsel auf einen anderen Presenter dienen die Methoden redirect() und forward(). Sie haben eine
sehr ähnliche Syntax wie die Methode link().
Die Methode forward() wechselt ohne HTTP-Weiterleitung sofort auf den neuen Presenter:
$this->forward('Product:show');
Beispiel für eine temporäre Weiterleitung mit dem HTTP-Code 302 (oder 303, wenn die aktuelle Request-Methode POST ist):
$this->redirect('Product:show', $id);
Für eine dauerhafte Weiterleitung mit dem HTTP-Code 301 verwenden Sie dies:
$this->redirectPermanent('Product:show', $id);
Auf eine andere URL außerhalb der Anwendung leiten Sie mit der Methode redirectUrl() weiter. Als zweiter
Parameter lässt sich der HTTP-Code angeben, Standard ist 302 (oder 303, wenn die aktuelle Request-Methode POST ist):
$this->redirectUrl('https://nette.org');
Eine Weiterleitung beendet die Tätigkeit des Presenters sofort, indem sie die sogenannte stille Beendigungs-Exception
Nette\Application\AbortException wirft.
Vor der Weiterleitung lassen sich Flash-Meldungen senden, also Meldungen, die nach der Weiterleitung im Template angezeigt werden.
Flash-Meldungen
Das sind Meldungen, die typischerweise über das Ergebnis einer Operation informieren. Eine wichtige Eigenschaft von Flash-Meldungen ist, dass sie im Template auch nach einer Weiterleitung verfügbar bleiben. Nach der Anzeige bleiben sie noch 30 Sekunden aktiv – lädt der Benutzer die Seite zum Beispiel wegen eines Übertragungsfehlers neu, verschwindet die Meldung nicht sofort.
Rufen Sie einfach die Methode flashMessage() auf, um
die Übergabe an das Template kümmert sich der Presenter. Der erste Parameter ist der Text der Meldung, der optionale zweite
Parameter ihr Typ (z. B. error, warning, info). Die Methode flashMessage() gibt eine Instanz der Flash-Meldung
zurück, der sich weitere Informationen hinzufügen lassen.
$this->flashMessage('Das Element wurde gelöscht.');
$this->redirect(/* ... */); // und weiterleiten
Im Template stehen diese Meldungen in der Variablen $flashes als Objekte stdClass zur Verfügung, die
die Properties message (Text der Meldung) und type (Typ der Meldung) sowie gegebenenfalls die erwähnten
eigenen Informationen enthalten. Wir rendern sie so:
{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
Fehler 404 usw.
Kann der Request nicht erfüllt werden, zum Beispiel weil der Artikel, den wir anzeigen wollen, nicht in der Datenbank
existiert, werfen wir mit der Methode error(string $message = '', int $httpCode = 404) einen Fehler 404.
public function renderShow(int $id): void
{
$article = $this->articles->getById($id);
if (!$article) {
$this->error();
}
// ...
}
Der HTTP-Fehlercode lässt sich als zweiter Parameter übergeben, Standard ist 404. Die Methode funktioniert so, dass sie eine
Nette\Application\BadRequestException wirft, woraufhin die Application die Steuerung an den
Error-Presenter übergibt. Das ist ein Presenter, dessen Aufgabe es ist, eine Seite mit Informationen über den aufgetretenen
Fehler anzuzeigen. Der Error-Presenter wird in der Konfiguration der
Anwendung eingestellt.
JSON senden
Die Methode sendJson($data) kodiert die übergebenen Daten in JSON, sendet sie als HTTP-Antwort und beendet den
Presenter. Beispiel:
public function actionData(): void
{
$data = ['hello' => 'nette'];
$this->sendJson($data);
}
Parameter des Requests
Der Presenter und ebenso jede Komponente beziehen ihre Parameter aus dem HTTP-Request. Ihre Werte holen Sie sich mit den
Methoden getParameter($name) oder getParameters(). Die Werte sind Strings oder Arrays von Strings, im
Grunde rohe Daten direkt aus der URL.
Für mehr Komfort empfehlen wir, auf die Parameter über Properties zuzugreifen. Kennzeichnen Sie sie einfach mit dem Attribut
#[Parameter]:
use Nette\Application\Attributes\Parameter; // diese Zeile ist wichtig
class HomePresenter extends Nette\Application\UI\Presenter
{
#[Parameter]
public string $theme; // muss public sein
}
Für die Property empfehlen wir, den Datentyp anzugeben (z. B. string), Nette wandelt den Wert dann automatisch
entsprechend um. Die Werte der Parameter lassen sich auch validieren.
Beim Erstellen eines Links lässt sich der Wert des Parameters direkt setzen:
<a n:href="Home:default theme: dark">klicken</a>
Persistente Parameter
Persistente Parameter dienen dazu, den Zustand über verschiedene Requests hinweg zu erhalten. Ihr Wert bleibt auch nach dem
Klick auf einen Link derselbe. Anders als Daten in der Session werden sie in der URL übertragen. Und das geschieht
vollautomatisch, sie müssen also nicht ausdrücklich in link() oder n:href angegeben werden.
Ein Anwendungsbeispiel? Stellen Sie sich vor, Sie haben eine mehrsprachige Anwendung. Die aktuelle Sprache ist ein Parameter,
der immer Teil der URL sein muss. Aber ihn in jedem Link anzugeben wäre unglaublich mühsam. Also machen Sie daraus den
persistenten Parameter lang, und er wird automatisch mitgeführt. Praktisch!
Einen persistenten Parameter in Nette zu erstellen ist ausgesprochen einfach. Legen Sie einfach eine public Property an und
kennzeichnen Sie sie mit dem Attribut: (früher wurde /** @persistent */ verwendet)
use Nette\Application\Attributes\Persistent; // diese Zeile ist wichtig
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang; // muss public sein
}
Hat $this->lang zum Beispiel den Wert 'en', enthalten auch die mit link() oder
n:href erstellten Links den Parameter lang=en. Und nach dem Klick auf den Link ist
$this->lang wieder 'en'.
Für die Property empfehlen wir, den Datentyp anzugeben (z. B. string), und Sie können auch einen Standardwert
angeben. Die Werte der Parameter lassen sich validieren.
Persistente Parameter werden üblicherweise zwischen allen Aktionen eines bestimmten Presenters übertragen. Damit sie auch über mehrere Presenter hinweg übertragen werden, müssen sie definiert werden entweder:
- in einem gemeinsamen Vorfahren, von dem die Presenter erben
- oder in einem Trait, den die Presenter verwenden:
trait LanguageAware
{
#[Persistent]
public string $lang;
}
class ProductPresenter extends Nette\Application\UI\Presenter
{
use LanguageAware;
}
Beim Erstellen eines Links lässt sich der Wert eines persistenten Parameters ändern:
<a n:href="Product:show $id, lang: cs">Detail auf Tschechisch</a>
Oder er lässt sich zurücksetzen, also aus der URL entfernen. Er nimmt dann seinen Standardwert an:
<a n:href="Product:show $id, lang: null">klicken</a>
Gemeinsamer Parameterraum
Die Parameter des Requests, die persistenten Parameter und die Parameter der Methoden
action, render und handle (Signal) teilen sich einen einzigen Raum, in dem jeder über
seinen Namen identifiziert wird. Erscheint derselbe Name in mehreren von ihnen, bezeichnen sie ein und denselben Wert.
Das nutzt man oft zum Vorteil. Zum Beispiel sind der persistente Parameter lang und das Argument
$lang einer Action- oder Signal-Methode ein und dasselbe – Sie können den aktuellen Wert eines persistenten
Parameters lesen, indem Sie ihn einfach in der Signatur der Methode aufführen:
#[Persistent]
public string $lang;
public function handleSearch(string $query, string $lang): void
{
// $lang enthält den aktuellen Wert des persistenten Parameters lang
}
Weil dieser Raum gemeinsam ist, halten Sie die Namen der Parameter eindeutig, sofern sie sich nicht absichtlich einen Wert teilen sollen. Das gilt auch für Signale, die zusätzlich Parameter aus dem POST-Body des Requests lesen, siehe Signale im Detail.
Interaktive Komponenten
Presenter haben ein eingebautes Komponentensystem. Komponenten sind eigenständige, wiederverwendbare Einheiten, die wir in Presenter einbetten. Das können Formulare sein, Datagrids, Menüs – kurz alles, was sich sinnvoll wiederholt verwenden lässt.
Wie werden Komponenten in Presenter eingebettet und anschließend verwendet? Das erfahren Sie im Kapitel Komponenten. Sie erfahren dort sogar, was sie mit Hollywood gemeinsam haben.
Und woher bekomme ich Komponenten? Auf Componette finden Sie Open-Source-Komponenten und viele weitere Erweiterungen für Nette, beigesteuert von Freiwilligen aus der Community des Frameworks.
Tiefer eintauchen
Was wir in diesem Kapitel bisher behandelt haben, dürfte für die meisten Anwendungsfälle genügen. Die folgenden Abschnitte sind für alle gedacht, die sich intensiver mit Presentern beschäftigen und absolut alles wissen wollen.
Validierung der Parameter
Die aus der URL empfangenen Werte der Parameter des Requests und Persistente Parameter werden von der Methode loadState() in die Properties
geschrieben. Sie prüft außerdem, ob der in der Property angegebene Datentyp passt; andernfalls antwortet sie mit dem Fehler
404 und die Seite wird nicht angezeigt.
Vertrauen Sie den aus der URL empfangenen Parametern niemals blind, denn sie lassen sich vom Benutzer leicht überschreiben. So
würden wir zum Beispiel prüfen, ob die Sprache $this->lang zu den unterstützten gehört. Ein geeigneter Weg
dafür ist, die erwähnte Methode loadState() zu überschreiben:
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang;
public function loadState(array $params): void
{
parent::loadState($params); // hier wird $this->lang gesetzt
// es folgt die eigene Wertprüfung:
if (!in_array($this->lang, ['en', 'cs'])) {
$this->error();
}
}
}
Request speichern und wiederherstellen
Der vom Presenter bearbeitete Request ist ein Objekt Nette\Application\Request, das die Methode
getRequest() des Presenters zurückgibt.
Der aktuelle Request lässt sich in der Session speichern oder umgekehrt aus ihr wiederherstellen und vom Presenter erneut
ausführen. Das ist zum Beispiel nützlich, wenn ein Benutzer ein Formular ausfüllt und seine Anmeldung abläuft. Damit keine
Daten verloren gehen, speichern wir den aktuellen Request vor der Weiterleitung auf die Anmeldeseite mit
$reqId = $this->storeRequest() in der Session. Das gibt seinen Bezeichner als kurzen String zurück, den wir dann
als Parameter an den Anmelde-Presenter übergeben.
Nach der Anmeldung rufen wir die Methode $this->restoreRequest($reqId) auf, die den Request aus der Session
holt. POST-Requests werden an ihn weitergeleitet, die übrigen (GET) werden auf die URL des Requests weitergeleitet. Die Methode
prüft, ob der Request von demselben Benutzer erstellt wurde, der nun angemeldet ist. Meldet sich ein anderer Benutzer an oder ist
der Schlüssel ungültig, tut sie nichts und das Programm läuft wie gewohnt weiter.
Siehe die Anleitung Wie man zu einer früheren Seite zurückkehrt.
Kanonisierung
Presenter haben eine wirklich ausgezeichnete Eigenschaft, die zu besserem SEO (Search Engine Optimization) beiträgt. Sie
verhindern automatisch, dass derselbe Inhalt unter verschiedenen URLs existiert. Führen mehrere URLs zu einem bestimmten Ziel, z.
B. /index und /index?page=1, bestimmt das Framework eine davon als primär (kanonisch) und leitet die
übrigen mit dem HTTP-Code 301 dorthin weiter. Dadurch indexieren Suchmaschinen Ihre Seiten nicht doppelt und verwässern deren
Page Rank nicht.
Dieser Vorgang heißt Kanonisierung. Die kanonische URL ist diejenige, die der Router erzeugt, typischerweise die erste passende Route in der Collection.
Die Kanonisierung ist standardmäßig eingeschaltet und lässt sich mit $this->autoCanonicalize = false
abschalten.
Bei AJAX- oder POST-Requests findet keine Weiterleitung statt, da dies zu Datenverlust führen könnte oder keinen zusätzlichen SEO-Nutzen brächte.
Die Kanonisierung lässt sich auch manuell mit der Methode canonicalize() auslösen. Ähnlich wie der Methode
link() übergeben Sie ihr den Presenter, die Aktion und die Parameter. Sie erzeugt einen Link und vergleicht ihn mit
der aktuellen URL-Adresse. Unterscheiden sie sich, leitet sie auf den erzeugten Link weiter.
public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// leitet weiter, wenn $slug von $realSlug abweicht
$this->canonicalize('Product:show', [$id, $realSlug]);
}
Ein vollständiges Muster, das Route-Filter mit canonicalize() kombiniert, um SEO-freundliche URLs zu erzeugen,
finden Sie unter Schöne URLs mit Slugs.
Antworten
Die vom Presenter zurückgegebene Antwort ist ein Objekt, das das Interface Nette\Application\Response implementiert. Es stehen mehrere fertige Antworten zur Verfügung:
- Nette\Application\Responses\CallbackResponse – sendet einen Callback
- Nette\Application\Responses\FileResponse – sendet eine Datei
- Nette\Application\Responses\ForwardResponse – forward()
- Nette\Application\Responses\JsonResponse – sendet JSON
- Nette\Application\Responses\RedirectResponse – Weiterleitung
- Nette\Application\Responses\TextResponse – sendet Text
- Nette\Application\Responses\VoidResponse – leere Antwort
Antworten werden mit der Methode sendResponse() gesendet:
use Nette\Application\Responses;
// Reiner Text
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));
// Sendet eine Datei
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));
// Sendet einen Callback
$callback = function (Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) {
if ($httpResponse->getHeader('Content-Type') === 'text/html') {
echo '<h1>Hallo</h1>';
}
};
$this->sendResponse(new Responses\CallbackResponse($callback));
Sie können auch eine eigene Antwort schreiben. Implementieren Sie einfach das Interface
Nette\Application\Response, das eine einzige Methode send() hat, die den HTTP-Request und die
HTTP-Response entgegennimmt. Das ist zum Beispiel nützlich, wenn Sie Daten streamen, die Sie nicht im Speicher halten wollen:
class CsvResponse implements Nette\Application\Response
{
public function __construct(
private string $fileName,
private iterable $rows,
) {
}
public function send(Nette\Http\IRequest $request, Nette\Http\IResponse $response): void
{
$response->setContentType('text/csv', 'utf-8');
$response->sendAsFile($this->fileName);
$handle = fopen('php://output', 'w');
foreach ($this->rows as $row) {
fputcsv($handle, $row);
}
fclose($handle);
}
}
Im Presenter senden Sie sie dann wie gewohnt: $this->sendResponse(new CsvResponse('export.csv', $rows));
HTTP-Caching
Die Methode lastModified() macht es leicht, HTTP-Caching zu nutzen. Sie übergeben ihr Datum und Uhrzeit der
letzten Änderung des Inhalts (als Timestamp, String oder Objekt DateTimeInterface) und optional einen ETag-Validator
(einen kurzen String, der die aktuelle Version des Inhalts identifiziert, etwa deren Hash) sowie eine Ablaufzeit. Hat der Browser
bereits eine passende Version, sendet der Presenter die Antwort 304 Not Modified und endet, sodass die Seite nicht
unnötig gerendert und übertragen wird:
public function renderArticle(int $id): void
{
$article = $this->articles->getById($id);
$this->lastModified($article->updatedAt);
// ...
}
Fertigstellung des Templates
Wenn der Presenter ein Template rendert, ruft die Methode sendTemplate() unmittelbar vor dem Rendern
completeTemplate() auf. Diese Methode füllt die mit dem Attribut #[TemplateVariable] gekennzeichneten
Variablen und ermittelt die Template-Datei (die Standardvariablen setzt bereits die TemplateFactory beim Erzeugen des
Templates). Sie können diese protected-Methode überschreiben, um Variablen zu ergänzen, die alle Views gemeinsam haben, oder um
eine andere Datei zu setzen:
protected function completeTemplate(Nette\Application\UI\Template $template): void
{
parent::completeTemplate($template);
$template->siteName = 'Meine App';
}
Zugriffsbeschränkung mit #[Requires]
Das Attribut #[Requires] bietet fortgeschrittene Möglichkeiten, den Zugriff auf Presenter und deren Methoden
einzuschränken. Damit lassen sich HTTP-Methoden festlegen, ein AJAX-Request verlangen, der Zugriff auf denselben Origin
beschränken und der Zugriff nur über Forwarding erlauben. Das Attribut lässt sich sowohl auf Presenter-Klassen als auch auf
einzelne Methoden wie action<Action>(), render<View>(), handle<Signal>()
und createComponent<Name>() anwenden.
Sie können diese Einschränkungen angeben:
- auf HTTP-Methoden:
#[Requires(methods: ['GET', 'POST'])] - Erfordernis eines AJAX-Requests:
#[Requires(ajax: true)] - Zugriff nur vom selben Origin:
#[Requires(sameOrigin: true)] - Zugriff nur über Forwarding:
#[Requires(forward: true)] - Einschränkung auf bestimmte Aktionen:
#[Requires(actions: 'default')]
Seit Version 3.3 wird die Übereinstimmung des Origins über den Browser-Header Sec-Fetch-Site
geprüft (früher über ein SameSite-Cookie), was zuverlässiger ist und die exakte Übereinstimmung von Schema, Domain und
Port prüft.
Details finden Sie in der Anleitung Wie man das Attribut Requires verwendet.
Prüfung der HTTP-Methode
Presenter in Nette prüfen bei jedem eingehenden Request automatisch die HTTP-Methode, vor allem aus Sicherheitsgründen.
Standardmäßig sind die Methoden GET, POST, HEAD, PUT, DELETE,
PATCH erlaubt.
Wollen Sie zusätzlich zum Beispiel die Methode OPTIONS erlauben, verwenden Sie das Attribut
#[Requires] (seit Nette Application v3.2.3):
#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
class MyPresenter extends Nette\Application\UI\Presenter
{
}
Seit Version 3.1.13 erfolgt die Prüfung in checkHttpMethod(), die prüft, ob die im Request angegebene Methode
im Array $presenter->allowedMethods enthalten ist. Seit Version 3.2.3 ist dieser Ansatz zugunsten von
#[Requires] veraltet. Die Methode können Sie so überschreiben:
class MyPresenter extends Nette\Application\UI\Presenter
{
protected function checkHttpMethod(): void
{
$this->allowedMethods[] = 'OPTIONS';
parent::checkHttpMethod();
}
}
Wichtig ist zu betonen: Wenn Sie die Methode OPTIONS aktivieren, müssen Sie sie anschließend in Ihrem Presenter
auch angemessen behandeln. Diese Methode wird oft als sogenannter Preflight-Request verwendet, den der Browser automatisch vor dem
eigentlichen Request sendet, wenn festgestellt werden muss, ob der Request nach der CORS-Richtlinie (Cross-Origin Resource
Sharing) zulässig ist. Aktivieren Sie die Methode, ohne die richtige Antwort zu implementieren, kann das zu Inkonsistenzen und
potenziellen Sicherheitsproblemen führen.
Veraltete Aktionen kennzeichnen
Das Attribut #[Deprecated] kennzeichnet Aktionen, Signale oder ganze Presenter als veraltet und für eine
künftige Entfernung vorgesehen. Beim Erzeugen von Links auf veraltete Teile der Anwendung wirft Nette eine Warnung, um die
Entwickler darauf aufmerksam zu machen.
Das Attribut lässt sich entweder auf die gesamte Presenter-Klasse oder auf einzelne Methoden
action<Action>(), render<View>() und handle<Signal>() anwenden.