Routing
Der Router kümmert sich um alles rund um URL-Adressen, damit Sie nicht mehr darüber nachdenken müssen. Wir zeigen Ihnen:
- wie man den Router einstellt, damit die URLs den Vorstellungen entsprechen
- wir sprechen über SEO und Weiterleitungen
- und zeigen Ihnen, wie Sie einen eigenen Router schreiben
Menschenfreundlichere URLs (oder auch coole oder pretty URLs) sind benutzbarer, leichter zu merken und tragen positiv zur SEO bei. Nette berücksichtigt dies und kommt Entwicklern voll entgegen. Sie können für Ihre Anwendung genau die Struktur der URL-Adressen entwerfen, die Sie möchten. Sie können sie sogar erst dann entwerfen, wenn die Anwendung bereits fertig ist, da dies ohne Eingriffe in den Code oder die Templates auskommt. Sie wird nämlich auf elegante Weise an einer einzigen Stelle definiert, im Router, und ist somit nicht in Form von Annotationen in allen Presentern verstreut.
Der Router in Nette ist außergewöhnlich, da er bidirektional ist. Er kann sowohl URLs in einer HTTP-Anfrage dekodieren als auch Links erstellen. Er spielt daher eine entscheidende Rolle in Nette Application, da er einerseits entscheidet, welcher Presenter und welche Aktion die aktuelle Anfrage ausführen wird, andererseits aber auch für die Generierung von URLs im Template usw. verwendet wird.
Der Router ist jedoch nicht nur auf diese Verwendung beschränkt, Sie können ihn auch in Anwendungen einsetzen, in denen überhaupt keine Presenter verwendet werden, für REST-APIs usw. Mehr dazu im Abschnitt Eigenständige Verwendung.
Routen-Sammlung
Die angenehmste Art, die Form der URL-Adressen in der Anwendung zu definieren, bietet die Klasse Nette\Application\Routers\RouteList. Die Definition besteht aus einer Liste sogenannter Routen, d. h. Masken von URL-Adressen und den ihnen zugeordneten Presentern und Aktionen über eine einfache API. Wir müssen die Routen nicht benennen.
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
Das Beispiel besagt, dass wenn wir im Browser https://domain.com/rss.xml
öffnen, der Presenter Feed
mit der Aktion rss
angezeigt wird, wenn https://domain.com/article/12
, wird der Presenter
Article
mit der Aktion view
angezeigt usw. Wenn keine passende Route gefunden wird, reagiert Nette
Application mit dem Auslösen einer BadRequestException, die dem
Benutzer als Fehlerseite 404 Not Found angezeigt wird.
Reihenfolge der Routen
Ganz entscheidend ist die Reihenfolge, in der die einzelnen Routen aufgeführt sind, da sie schrittweise von oben nach unten ausgewertet werden. Es gilt die Regel, dass wir Routen von spezifisch nach allgemein deklarieren:
// FALSCH: 'rss.xml' wird von der ersten Route abgefangen und diese Zeichenkette als <slug> verstanden
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// GUT
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
Routen werden auch bei der Generierung von Links von oben nach unten ausgewertet:
// FALSCH: Ein Link zu 'Feed:rss' wird als 'admin/feed/rss' generiert
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');
// GUT
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
Wir werden Ihnen nicht verheimlichen, dass die korrekte Zusammenstellung der Routen eine gewisse Fertigkeit erfordert. Bevor Sie diese beherrschen, wird Ihnen das Routing-Panel ein nützlicher Helfer sein.
Maske und Parameter
Die Maske beschreibt den relativen Pfad vom Stammverzeichnis der Website. Die einfachste Maske ist eine statische URL:
$router->addRoute('products', 'Products:default');
Oft enthalten Masken sogenannte Parameter. Diese werden in spitzen Klammern angegeben (z. B. <year>
)
und an den Ziel-Presenter übergeben, beispielsweise an die Methode renderShow(int $year)
oder an den persistenten
Parameter $year
:
$router->addRoute('chronicle/<year>', 'History:show');
Das Beispiel besagt, dass wenn wir im Browser https://example.com/chronicle/2020
öffnen, der Presenter
History
mit der Aktion show
und dem Parameter year: 2020
angezeigt wird.
Wir können Parametern direkt in der Maske einen Standardwert zuweisen, wodurch sie optional werden:
$router->addRoute('chronicle/<year=2020>', 'History:show');
Die Route akzeptiert nun auch die URL https://example.com/chronicle/
, die wiederum History:show
mit
dem Parameter year: 2020
anzeigt.
Parameter können natürlich auch der Name des Presenters und der Aktion sein. Zum Beispiel so:
$router->addRoute('<presenter>/<action>', 'Home:default');
Die angegebene Route akzeptiert z. B. URLs in der Form /article/edit
oder auch /catalog/list
und
versteht sie als Presenter und Aktionen Article:edit
und Catalog:list
.
Gleichzeitig gibt sie den Parametern presenter
und action
die Standardwerte Home
und
default
, wodurch sie ebenfalls optional sind. Die Route akzeptiert also auch eine URL in der Form
/article
und versteht sie als Article:default
. Oder umgekehrt, ein Link zu Product:default
generiert den Pfad /product
, ein Link zum Standard Home:default
den Pfad /
.
Die Maske kann nicht nur den relativen Pfad vom Stammverzeichnis der Website beschreiben, sondern auch einen absoluten Pfad, wenn sie mit einem Schrägstrich beginnt, oder sogar eine gesamte absolute URL, wenn sie mit zwei Schrägstrichen beginnt:
// relativ zum Document Root
$router->addRoute('<presenter>/<action>', /* ... */);
// absoluter Pfad (relativ zur Domain)
$router->addRoute('/<presenter>/<action>', /* ... */);
// absolute URL inklusive Domain (relativ zum Schema)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// absolute URL inklusive Schema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
Validierungsausdrücke
Für jeden Parameter kann eine Validierungsbedingung mithilfe eines Regulären Ausdrucks festgelegt werden. Zum Beispiel
bestimmen wir für den Parameter id
, dass er nur Ziffern annehmen darf, mit dem Regulären Ausdruck
\d+
:
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
Der standardmäßige reguläre Ausdruck für alle Parameter ist [^/]+
, d. h. alles außer einem Schrägstrich.
Wenn ein Parameter auch Schrägstriche akzeptieren soll, geben wir den Ausdruck .+
an:
// akzeptiert https://example.com/a/b/c, path wird 'a/b/c'
$router->addRoute('<path .+>', /* ... */);
Optionale Sequenzen
In der Maske können optionale Teile mithilfe von eckigen Klammern markiert werden. Jeder Teil der Maske kann optional sein, und er kann auch Parameter enthalten:
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// Akzeptiert Pfade:
// /cs/download => lang => cs, name => download
// /download => lang => null, name => download
Wenn ein Parameter Teil einer optionalen Sequenz ist, wird er natürlich auch optional. Wenn kein Standardwert angegeben ist, wird er null sein.
Optionale Teile können auch in der Domain vorkommen:
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
Sequenzen können beliebig verschachtelt und kombiniert werden:
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// Akzeptiert Pfade:
// /cs/hello
// /en-us/hello
// /hello
// /hello/page-12
Bei der URL-Generierung wird die kürzeste Variante angestrebt, sodass alles, was weggelassen werden kann, weggelassen wird.
Daher generiert beispielsweise die Route index[.html]
den Pfad /index
. Das Verhalten kann durch Angabe
eines Ausrufezeichens nach der linken eckigen Klammer umgekehrt werden:
// akzeptiert /hello und /hello.html, generiert /hello
$router->addRoute('<name>[.html]', /* ... */);
// akzeptiert /hello und /hello.html, generiert /hello.html
$router->addRoute('<name>[!.html]', /* ... */);
Optionale Parameter (d. h. Parameter mit einem Standardwert) ohne eckige Klammern verhalten sich im Grunde so, als wären sie wie folgt geklammert:
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// entspricht diesem:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
Wenn wir das Verhalten des abschließenden Schrägstrichs beeinflussen möchten, damit z. B. anstelle von /home/
nur /home
generiert wird, kann dies wie folgt erreicht werden:
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
Platzhalter
In der Maske des absoluten Pfads können wir folgende Platzhalter verwenden, um beispielsweise die Notwendigkeit zu vermeiden, die Domain in die Maske zu schreiben, die sich in der Entwicklungs- und Produktionsumgebung unterscheiden kann:
%tld%
= Top-Level-Domain, z. B.com
oderorg
%sld%
= Second-Level-Domain, z. B.example
%domain%
= Domain ohne Subdomains, z. B.example.com
%host%
= Gesamter Host, z. B.www.example.com
%basePath%
= Pfad zum Stammverzeichnis
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);
Erweiterte Notation
Das Ziel der Route, normalerweise in der Form Presenter:action
geschrieben, kann auch mithilfe eines Arrays
notiert werden, das die einzelnen Parameter und ihre Standardwerte definiert:
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
'presenter' => 'Home',
'action' => 'default',
]);
Für eine detailliertere Spezifikation kann eine noch erweiterte Form verwendet werden, in der wir neben den Standardwerten
auch andere Eigenschaften der Parameter einstellen können, wie z. B. den Validierungs-regulären Ausdruck (siehe Parameter
id
):
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>[/<id>]', [
'presenter' => [
Route::Value => 'Home',
],
'action' => [
Route::Value => 'default',
],
'id' => [
Route::Pattern => '\d+',
],
]);
Es ist wichtig zu beachten, dass wenn die im Array definierten Parameter nicht in der Pfadmaske aufgeführt sind, ihre Werte nicht geändert werden können, auch nicht durch Query-Parameter, die nach dem Fragezeichen in der URL aufgeführt sind.
Filter und Übersetzungen
Wir schreiben den Quellcode der Anwendung auf Englisch, aber wenn die Website deutsche URLs haben soll, dann wird einfaches Routing vom Typ:
$router->addRoute('<presenter>/<action>', 'Home:default');
englische URLs generieren, wie z. B. /product/123
oder /cart
. Wenn wir Presenter und Aktionen in der
URL durch deutsche Wörter repräsentieren lassen wollen (z. B. /produkt/123
oder /warenkorb
), können
wir ein Übersetzungswörterbuch verwenden. Für dessen Notation benötigen wir bereits die “gesprächigere” Variante des
zweiten Parameters:
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterTable => [
// Zeichenkette in der URL => Presenter
'produkt' => 'Product',
'warenkorb' => 'Cart',
'katalog' => 'Catalog',
],
],
'action' => [
Route::Value => 'default',
Route::FilterTable => [
'liste' => 'list',
],
],
]);
Mehrere Schlüssel des Übersetzungswörterbuchs können auf denselben Presenter verweisen. Dadurch werden verschiedene Aliase dafür erstellt. Als kanonische Variante (also diejenige, die in der generierten URL enthalten sein wird) gilt der letzte Schlüssel.
Die Übersetzungstabelle kann auf diese Weise für jeden Parameter verwendet werden. Wobei, wenn die Übersetzung nicht
existiert, der ursprüngliche Wert genommen wird. Dieses Verhalten können wir durch Hinzufügen von
Route::FilterStrict => true
ändern, und die Route lehnt dann die URL ab, wenn der Wert nicht im Wörterbuch
enthalten ist.
Neben dem Übersetzungswörterbuch in Form eines Arrays können auch eigene Übersetzungsfunktionen eingesetzt werden.
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>/<id>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterIn => function (string $s): string { /* ... */ },
Route::FilterOut => function (string $s): string { /* ... */ },
],
'action' => 'default',
'id' => null,
]);
Die Funktion Route::FilterIn
konvertiert zwischen dem Parameter in der URL und der Zeichenkette, die dann an den
Presenter übergeben wird, die Funktion FilterOut
stellt die Konvertierung in die entgegengesetzte Richtung
sicher.
Die Parameter presenter
, action
und module
haben bereits vordefinierte Filter, die
zwischen dem PascalCase- bzw. camelCase-Stil und dem in URLs verwendeten kebab-case konvertieren. Der Standardwert der Parameter
wird bereits in transformierter Form geschrieben, sodass wir beispielsweise im Fall des Presenters
<presenter=ProductEdit>
schreiben, nicht <presenter=product-edit>
.
Allgemeine Filter
Neben Filtern, die für spezifische Parameter bestimmt sind, können wir auch allgemeine Filter definieren, die ein
assoziatives Array aller Parameter erhalten, die sie beliebig modifizieren und dann zurückgeben können. Allgemeine Filter
definieren wir unter dem Schlüssel null
.
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => 'Home',
'action' => 'default',
null => [
Route::FilterIn => function (array $params): array { /* ... */ },
Route::FilterOut => function (array $params): array { /* ... */ },
],
]);
Allgemeine Filter bieten die Möglichkeit, das Verhalten der Route auf beliebige Weise anzupassen. Wir können sie
beispielsweise zur Modifikation von Parametern basierend auf anderen Parametern verwenden. Zum Beispiel die Übersetzung von
<presenter>
und <action>
basierend auf dem aktuellen Wert des Parameters
<lang>
.
Wenn ein Parameter einen eigenen Filter definiert hat und gleichzeitig ein allgemeiner Filter existiert, wird der eigene
FilterIn
vor dem allgemeinen ausgeführt und umgekehrt der allgemeine FilterOut
vor dem eigenen. Das
heißt, innerhalb des allgemeinen Filters sind die Werte der Parameter presenter
bzw. action
im
PascalCase- bzw. camelCase-Stil geschrieben.
Einwegrouten (OneWay)
Einwegrouten werden verwendet, um die Funktionalität alter URLs beizubehalten, die die Anwendung nicht mehr generiert, aber
weiterhin akzeptiert. Wir markieren sie mit dem Flag OneWay
:
// alte URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// neue URL /product/123
$router->addRoute('product/<id>', 'Product:detail');
Beim Zugriff auf die alte URL leitet der Presenter automatisch auf die neue URL weiter, sodass Suchmaschinen diese Seiten nicht doppelt indizieren (siehe SEO und Kanonisierung).
Dynamisches Routing mit Callbacks
Dynamisches Routing mit Callbacks ermöglicht es Ihnen, Routen direkt Funktionen (Callbacks) zuzuordnen, die ausgeführt werden, wenn der entsprechende Pfad besucht wird. Diese flexible Funktionalität ermöglicht es Ihnen, schnell und effizient verschiedene Endpunkte (Endpoints) für Ihre Anwendung zu erstellen:
$router->addRoute('test', function () {
echo 'Sie befinden sich unter der Adresse /test';
});
Sie können auch Parameter in der Maske definieren, die automatisch an Ihren Callback übergeben werden:
$router->addRoute('<lang cs|en>', function (string $lang) {
echo match ($lang) {
'cs' => 'Willkommen auf der tschechischen Version unserer Website!',
'en' => 'Welcome to the English version of our website!',
};
});
Module
Wenn wir mehrere Routen haben, die zu einem gemeinsamen Modul gehören, verwenden wir
withModule()
:
$router = new RouteList;
$router->withModule('Forum') // Die folgenden Routen sind Teil des Moduls Forum
->addRoute('rss', 'Feed:rss') // Der Presenter wird Forum:Feed sein
->addRoute('<presenter>/<action>')
->withModule('Admin') // Die folgenden Routen sind Teil des Moduls Forum:Admin
->addRoute('sign:in', 'Sign:in');
Eine Alternative ist die Verwendung des Parameters module
:
// Die URL manage/dashboard/default wird auf den Presenter Admin:Dashboard gemappt
$router->addRoute('manage/<presenter>/<action>', [
'module' => 'Admin',
]);
Subdomains
Routen-Sammlungen können nach Subdomains gegliedert werden:
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
Im Domainnamen können auch Platzhalter verwendet werden:
$router = new RouteList;
$router->withDomain('example.%tld%')
// ...
Pfadpräfix
Routen-Sammlungen können nach dem Pfad in der URL gegliedert werden:
$router = new RouteList;
$router->withPath('eshop')
->addRoute('rss', 'Feed:rss') // Fängt URL /eshop/rss ab
->addRoute('<presenter>/<action>'); // Fängt URL /eshop/<presenter>/<action> ab
Kombinationen
Die oben genannten Gliederungen können miteinander kombiniert werden:
$router = (new RouteList)
->withDomain('admin.example.com')
->withModule('Admin')
->addRoute(/* ... */)
->addRoute(/* ... */)
->end()
->withModule('Images')
->addRoute(/* ... */)
->end()
->end()
->withDomain('example.com')
->withPath('export')
->addRoute(/* ... */)
// ...
Query-Parameter
Masken können auch Query-Parameter enthalten (Parameter nach dem Fragezeichen in der URL). Für diese kann kein Validierungsausdruck definiert werden, aber der Name, unter dem sie an den Presenter übergeben werden, kann geändert werden:
// Den Query-Parameter 'cat' möchten wir in der Anwendung unter dem Namen 'categoryId' verwenden
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
Foo-Parameter
Jetzt gehen wir tiefer. Foo-Parameter sind im Grunde unbenannte Parameter, die es ermöglichen, einen regulären Ausdruck
abzugleichen. Ein Beispiel ist eine Route, die /index
, /index.html
, /index.htm
und
/index.php
akzeptiert:
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
Es kann auch explizit eine Zeichenkette definiert werden, die bei der URL-Generierung verwendet wird. Die Zeichenkette muss
direkt nach dem Fragezeichen platziert werden. Die folgende Route ähnelt der vorherigen, aber generiert /index.html
anstelle von /index
, da die Zeichenkette .html
als Generierungswert festgelegt ist:
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
Integration in die Anwendung
Um den erstellten Router in die Anwendung einzubinden, müssen wir den DI-Container darüber informieren. Der einfachste Weg
ist, eine Factory vorzubereiten, die das Router-Objekt erstellt, und dem Container in der Konfiguration mitzuteilen, dass er sie
verwenden soll. Nehmen wir an, wir schreiben zu diesem Zweck eine Methode App\Core\RouterFactory::createRouter()
:
namespace App\Core;
use Nette\Application\Routers\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute(/* ... */);
return $router;
}
}
In die Konfiguration schreiben wir dann:
services:
- App\Core\RouterFactory::createRouter
Jegliche Abhängigkeiten, zum Beispiel auf eine Datenbank usw., werden der Factory-Methode als ihre Parameter mittels Autowiring übergeben:
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
SimpleRouter
Ein viel einfacherer Router als die Routen-Sammlung ist SimpleRouter. Wir verwenden ihn,
wenn wir keine besonderen Anforderungen an die URL-Form haben, wenn mod_rewrite
(oder seine Alternativen) nicht
verfügbar ist oder wenn wir uns noch nicht mit schönen URLs befassen wollen.
Er generiert Adressen ungefähr in dieser Form:
http://example.com/?presenter=Product&action=detail&id=123
Der Parameter des SimpleRouter-Konstruktors ist der Standard-Presenter & die Standard-Aktion, auf den verwiesen werden
soll, wenn wir eine Seite ohne Parameter öffnen, z. B. http://example.com/
.
// Der Standard-Presenter wird 'Home' sein und die Aktion 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
Wir empfehlen, den SimpleRouter direkt in der Konfiguration zu definieren:
services:
- Nette\Application\Routers\SimpleRouter('Home:default')
SEO und Kanonisierung
Das Framework trägt zur SEO (Optimierung der Auffindbarkeit im Internet) bei, indem es doppelte Inhalte unter verschiedenen
URLs verhindert. Wenn mehrere Adressen zu einem bestimmten Ziel führen, z. B. /index
und /index.html
,
bestimmt das Framework die erste davon als primäre (kanonische) und leitet die anderen mit dem HTTP-Code 301 darauf um. Dadurch
indizieren Suchmaschinen die Seiten nicht doppelt und verwässern ihren Page Rank nicht.
Dieser Prozess wird Kanonisierung genannt. Die kanonische URL ist diejenige, die vom Router generiert wird, d. h. die erste passende Route in der Sammlung ohne das OneWay-Flag. Deshalb führen wir in der Sammlung die primären Routen zuerst auf.
Die Kanonisierung wird vom Presenter durchgeführt, mehr im Kapitel Kanonisierung.
HTTPS
Um das HTTPS-Protokoll verwenden zu können, ist es notwendig, es beim Hosting zu aktivieren und den Server korrekt zu konfigurieren.
Die Weiterleitung der gesamten Website auf HTTPS muss auf Serverebene eingestellt werden, zum Beispiel mithilfe der .htaccess-Datei im Stammverzeichnis unserer Anwendung, und zwar mit dem HTTP-Code 301. Die Einstellungen können je nach Hosting variieren und sehen etwa so aus:
<IfModule mod_rewrite.c>
RewriteEngine On
...
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
...
</IfModule>
Der Router generiert URLs mit demselben Protokoll, mit dem die Seite geladen wurde, daher muss nichts weiter eingestellt werden.
Wenn wir aber ausnahmsweise benötigen, dass verschiedene Routen unter verschiedenen Protokollen laufen, geben wir es in der Routenmaske an:
// Wird eine Adresse mit HTTP generieren
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);
// Wird eine Adresse mit HTTPS generieren
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
Debugging des Routers
Das Routing-Panel, das in der Tracy Bar angezeigt wird, ist ein nützlicher Helfer, der eine Liste der Routen sowie die Parameter anzeigt, die der Router aus der URL erhalten hat.
Der grüne Balken mit dem Symbol ✓ stellt die Route dar, die die aktuelle URL verarbeitet hat, mit blauer Farbe und dem Symbol ≈ sind Routen gekennzeichnet, die die URL ebenfalls verarbeitet hätten, wenn die grüne sie nicht überholt hätte. Weiterhin sehen wir den aktuellen Presenter & die aktuelle Aktion.

Gleichzeitig, wenn es zu einer unerwarteten Weiterleitung aufgrund der Kanonisierung kommt, ist es nützlich, in das Panel in der Leiste redirect zu schauen, wo Sie herausfinden, wie der Router die URL ursprünglich verstanden hat und warum er weitergeleitet hat.
Beim Debuggen des Routers empfehlen wir, die Developer Tools im Browser zu öffnen (Strg+Shift+I oder Cmd+Option+I) und im Network-Panel den Cache zu deaktivieren, damit Weiterleitungen nicht darin gespeichert werden.
Leistung
Die Anzahl der Routen beeinflusst die Geschwindigkeit des Routers. Ihre Anzahl sollte definitiv nicht mehrere Dutzend überschreiten. Wenn Ihre Website eine zu komplizierte URL-Struktur hat, können Sie einen maßgeschneiderten Eigener Router schreiben.
Wenn der Router keine Abhängigkeiten hat, zum Beispiel von einer Datenbank, und seine Factory keine Argumente entgegennimmt, können wir seine kompilierte Form direkt in den DI-Container serialisieren und dadurch die Anwendung geringfügig beschleunigen.
routing:
cache: true
Eigener Router
Die folgenden Zeilen sind für sehr fortgeschrittene Benutzer bestimmt. Sie können Ihren eigenen Router erstellen und ihn ganz natürlich in die Routen-Sammlung integrieren. Der Router ist eine Implementierung des Interfaces Nette\Routing\Router mit zwei Methoden:
use Nette\Http\IRequest as HttpRequest;
use Nette\Http\UrlScript;
class MyRouter implements Nette\Routing\Router
{
public function match(HttpRequest $httpRequest): ?array
{
// ...
}
public function constructUrl(array $params, UrlScript $refUrl): ?string
{
// ...
}
}
Die Methode match
verarbeitet die aktuelle Anfrage $httpRequest, aus der nicht nur die URL, sondern auch Header usw. abgerufen
werden können, in ein Array, das den Namen des Presenters und seine Parameter enthält. Wenn sie die Anfrage nicht verarbeiten
kann, gibt sie null zurück. Bei der Verarbeitung der Anfrage müssen wir mindestens den Presenter und die Aktion zurückgeben.
Der Name des Presenters ist vollständig und enthält auch eventuelle Module:
[
'presenter' => 'Front:Home',
'action' => 'default',
]
Die Methode constructUrl
erstellt umgekehrt aus dem Parameter-Array die resultierende absolute URL. Dazu kann sie
Informationen aus dem Parameter $refUrl
verwenden, was die aktuelle URL ist.
Zur Routen-Sammlung fügen Sie ihn mit add()
hinzu:
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
Eigenständige Verwendung
Unter eigenständiger Verwendung verstehen wir die Nutzung der Fähigkeiten des Routers in einer Anwendung, die Nette Application und Presenter nicht verwendet. Dafür gilt fast alles, was wir in diesem Kapitel gezeigt haben, mit diesen Unterschieden:
- für Routen-Sammlungen verwenden wir die Klasse Nette\Routing\RouteList
- als einfachen Router die Klasse Nette\Routing\SimpleRouter
- da das Paar
Presenter:action
nicht existiert, verwenden wir die Erweiterte Notation
Also erstellen wir wieder eine Methode, die uns den Router zusammenstellt, z.B.:
namespace App\Core;
use Nette\Routing\RouteList;
class RouterFactory
{
public static function createRouter(): RouteList
{
$router = new RouteList;
$router->addRoute('rss.xml', [
'controller' => 'RssFeedController',
]);
$router->addRoute('article/<id \d+>', [
'controller' => 'ArticleController',
]);
// ...
return $router;
}
}
Wenn Sie einen DI-Container verwenden, was wir empfehlen, fügen wir die Methode wieder zur Konfiguration hinzu und holen danach den Router zusammen mit der HTTP-Anfrage aus dem Container:
$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);
Oder wir erstellen die Objekte direkt:
$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
Jetzt muss der Router nur noch seine Arbeit aufnehmen:
$params = $router->match($httpRequest);
if ($params === null) {
// Es wurde keine passende Route gefunden, senden wir einen 404-Fehler
exit;
}
// Wir verarbeiten die erhaltenen Parameter
$controller = $params['controller'];
// ...
Und umgekehrt verwenden wir den Router, um einen Link zu erstellen:
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());