Routenplanung
Der Router ist für alles zuständig, was mit URLs zu tun hat, so dass Sie nicht mehr darüber nachdenken müssen. Das werden wir zeigen:
- wie man den Router so einrichtet, dass die URLs so aussehen, wie Sie es wünschen
- ein paar Hinweise zur SEO-Routing
- und wir zeigen Ihnen, wie Sie Ihren eigenen Router schreiben können
Menschlichere URLs (oder coole oder schöne URLs) sind benutzerfreundlicher, einprägsamer und tragen positiv zur Suchmaschinenoptimierung bei. Nette hat dies im Sinn und kommt den Wünschen der Entwickler voll entgegen. Sie können die URL-Struktur für Ihre Anwendung genau so gestalten, wie Sie es wünschen. Sie können sie sogar entwerfen, nachdem die Anwendung fertig ist, da dies ohne Code- oder Vorlagenänderungen möglich ist. Sie wird auf elegante Weise an einer einzigen Stelle, nämlich im Router, definiert und ist nicht in Form von Anmerkungen in allen Presentern verstreut.
Der Router in Nette ist etwas Besonderes, denn er ist bidirektional, er kann sowohl HTTP-Anfrage-URLs dekodieren als auch Links erstellen. Er spielt also eine wichtige Rolle in der Nette-Anwendung, denn er entscheidet, welcher Presenter und welche Aktion die aktuelle Anfrage ausführen wird, und er wird auch für die URL-Generierung in der Vorlage usw. verwendet.
Der Router ist jedoch nicht auf diese Verwendung beschränkt, sondern kann auch in Anwendungen eingesetzt werden, in denen Presenter überhaupt nicht verwendet werden, für REST-APIs usw. Mehr dazu im Abschnitt Getrennte Nutzung.
Routen-Sammlung
Die angenehmste Art, die URL-Adressen in der Anwendung zu definieren, ist über die Klasse Nette\Application\Routers\RouteList. Die Definition besteht aus einer Liste sogenannter Routen, d.h. Masken von URL-Adressen und den dazugehörigen 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 https://any-domain.com/rss.xml
mit der Aktion rss
angezeigt wird,
wenn https://domain.com/article/12
mit der Aktion view
angezeigt, usw. Wenn keine passende Route
gefunden wird, reagiert Nette Application mit der Ausnahme BadRequestException, die dem
Benutzer als 404 Not Found Fehlerseite angezeigt wird.
Reihenfolge der Routen
Die Reihenfolge, in der die Routen aufgelistet werden, ist sehr wichtig, da sie von oben nach unten ausgewertet werden. Die Regel lautet, dass wir Routen von spezifisch nach allgemein deklarieren:
// FALSCH: 'rss.xml' stimmt mit der ersten Route überein und missversteht diese als <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// GUT
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
Die Routen werden auch bei der Erstellung von Links von oben nach unten ausgewertet:
// FALSCH: erzeugt einen Link zu 'Feed:rss' als 'admin/feed/rss'
$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 wollen Ihnen nicht verschweigen, dass es einiges an Geschick erfordert, eine Liste richtig aufzubauen. Bis Sie sich damit vertraut gemacht haben, ist das Routing-Panel ein nützliches Werkzeug.
Maske und Parameter
Die Maske beschreibt den relativen Pfad auf der Grundlage des Stammverzeichnisses der Website. Die einfachste Maske ist eine statische URL:
$router->addRoute('products', 'Products:default');
Oft enthalten Masken sogenannte Parameter. Sie sind in spitzen Klammern eingeschlossen (z.B. <year>
)
und werden an den Zielmoderator übergeben, z. B. an die Methode renderShow(int $year)
oder an persistente Parameter
$year
:
$router->addRoute('chronicle/<year>', 'History:show');
Das Beispiel besagt, dass, wenn wir https://any-domain.com/chronicle/2020
und die Aktion show
mit dem
Parameter year: 2020
angezeigt werden.
Wir können direkt in der Maske einen Standardwert für die Parameter angeben, so dass sie optional werden:
$router->addRoute('chronicle/<year=2020>', 'History:show');
Die Route wird nun die URL https://any-domain.com/chronicle/
mit dem Parameter year: 2020
anzeigt.
Natürlich kann auch der Name des Präsentators und der Aktion ein Parameter sein. Zum Beispiel:
$router->addRoute('<presenter>/<action>', 'Home:default');
Diese Route nimmt z.B. eine URL in der Form /article/edit
bzw. /catalog/list
entgegen und übersetzt
sie in Präsentatoren und Aktionen Article:edit
bzw. Catalog:list
.
Außerdem werden den Parametern presenter
und action
die StandardwerteHome
und
default
zugewiesen, so dass sie optional sind. So akzeptiert die Route auch eine URL /article
und
übersetzt sie als Article:default
. Oder umgekehrt, ein Link auf Product:default
erzeugt einen Pfad
/product
, ein Link auf den Standardwert Home:default
erzeugt einen Pfad /
.
Die Maske kann nicht nur den relativen Pfad auf der Grundlage des Stammverzeichnisses der Website beschreiben, sondern auch den absoluten Pfad, wenn er mit einem Schrägstrich beginnt, oder sogar die gesamte absolute URL, wenn sie mit zwei Schrägstrichen beginnt:
// relativer Pfad zum Stamm des Anwendungsdokuments
$router->addRoute('<presenter>/<action>', /* ... */);
// absoluter Pfad, relativ zum Server-Hostnamen
$router->addRoute('/<presenter>/<action>', /* ... */);
// absolute URL einschließlich Hostname (aber schema-relativ)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// absolute URL einschließlich Schema
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
Validierungsausdrücke
Für jeden Parameter kann mit Hilfe eines regulären
Ausdrucks eine Validierungsbedingung angegeben werden. Ein Beispiel: id
soll nur numerisch sein, mit
\d+
regexp:
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
Der reguläre Standardausdruck für alle Parameter ist [^/]+
d.h. alles außer dem Schrägstrich. Wenn ein
Parameter auch auf einen Schrägstrich passen soll, setzen wir den regulären Ausdruck auf .+
.
// akzeptiert https://example.com/a/b/c, Pfad ist 'a/b/c'
$router->addRoute('<path .+>', /* ... */);
Optionale Sequenzen
Eckige Klammern kennzeichnen optionale Teile der Maske. Jeder Teil der Maske kann als optional festgelegt werden, auch die Teile, die Parameter enthalten:
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// Akzeptierte URLs: Parameter:
// /de/download lang => de, name => download
// /download lang => null, Name => download
Wenn ein Parameter Teil einer optionalen Sequenz ist, wird er natürlich auch optional. Wenn er keinen Standardwert hat, ist er null.
Optionale Abschnitte können sich auch in der Domäne befinden:
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
Sequenzen können frei verschachtelt und kombiniert werden:
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// Akzeptierte URLs:
// /de/hello
// /en-us/hello
// /hallo
// /hallo/seite-12
Der URL-Generator versucht, die URL so kurz wie möglich zu halten, so dass alles, was weggelassen werden kann, auch
weggelassen wird. Daher erzeugt zum Beispiel eine Route index[.html]
einen Pfad /index
erzeugt. Sie
können dieses Verhalten umkehren, indem Sie ein Ausrufezeichen hinter die linke eckige Klammer schreiben:
// akzeptiert sowohl /hello als auch /hello.html, erzeugt /hello
$router->addRoute('<name>[.html]', /* ... */);
// akzeptiert sowohl /hello als auch /hello.html, erzeugt /hello.html
$router->addRoute('<name>[!.html]', /* ... */);
Optionale Parameter (d.h. Parameter mit Standardwerten) ohne eckige Klammern verhalten sich so, als wären sie so umbrochen:
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// gleichbedeutend mit:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
Um zu ändern, wie der Schrägstrich ganz rechts erzeugt wird, d. h. statt /home/
ein /home
zu
erhalten, passen Sie die Route folgendermaßen an:
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
Platzhalter
In der absoluten Pfadmaske können wir die folgenden Platzhalter verwenden, um z. B. die Notwendigkeit zu vermeiden, eine Domäne in die Maske zu schreiben, die in der Entwicklungs- und Produktionsumgebung unterschiedlich sein kann:
%tld%
= Top-Level-Domain, z.B.com
oderorg
%sld%
= Domain der zweiten Ebene, z. B.example
%domain%
= Domain ohne Subdomains, z.B.example.com
%host%
= ganzer 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 einer Route, in der Regel in der Form Presenter:action
geschrieben, kann auch durch ein Array
ausgedrückt werden, das einzelne Parameter und ihre Standardwerte definiert:
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
'presenter' => 'Home',
'action' => 'default',
]);
Für eine detailliertere Spezifikation kann eine noch umfangreichere Form verwendet werden, bei der zusätzlich zu den
Standardwerten weitere Parametereigenschaften festgelegt werden können, wie z. B. ein regulärer Ausdruck für die Validierung
(siehe den 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 enthalten sind, ihre Werte nicht geändert werden können, auch nicht mit Abfrageparametern, die nach einem Fragezeichen in der URL angegeben werden.
Filter und Übersetzungen
Es ist eine gute Praxis, den Quellcode auf Englisch zu schreiben, aber was ist, wenn die URL Ihrer Website in eine andere Sprache übersetzt werden soll? Einfache Routen wie z.B.:
$router->addRoute('<presenter>/<action>', 'Home:default');
erzeugen englische URLs, wie /product/123
oder /cart
. Wenn wir Präsentatoren und Aktionen in der URL
ins Deutsche übersetzt haben wollen (z. B. /produkt/123
oder /einkaufswagen
), können wir ein
Übersetzungswörterbuch verwenden. Um es hinzuzufügen, benötigen wir bereits eine “gesprächigere” Variante des zweiten
Parameters:
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterTable => [
// String in URL => Präsentator
'produkt' => 'Product',
'einkaufswagen' => 'Cart',
'katalog' => 'Catalog',
],
],
'action' => [
Route::Value => 'default',
Route::FilterTable => [
'liste' => 'list',
],
],
]);
Es können mehrere Wörterbuchschlüssel für denselben Präsentator verwendet werden. Sie erzeugen dann verschiedene Aliase für ihn. Der letzte Schlüssel gilt als die kanonische Variante (d. h. diejenige, die in der generierten URL enthalten sein wird).
Die Übersetzungstabelle kann auf diese Weise auf jeden Parameter angewendet werden. Wenn die Übersetzung jedoch nicht
existiert, wird der ursprüngliche Wert genommen. Wir können dieses Verhalten ändern, indem wir
Route::FilterStrict => true
hinzufügen, und die Route wird dann die URL zurückweisen, wenn der Wert nicht im
Wörterbuch enthalten ist.
Zusätzlich zum Übersetzungswörterbuch in Form eines Arrays ist es möglich, eigene Übersetzungsfunktionen festzulegen:
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 dem String, der dann an den
Presenter übergeben wird, die Funktion FilterOut
sorgt für die Konvertierung in die umgekehrte Richtung.
Die Parameter presenter
, action
und module
haben bereits vordefinierte Filter, die
zwischen der in der URL verwendeten PascalCase- bzw. camelCase-Schreibweise und der Kebab-Case-Schreibweise konvertieren. Der
Standardwert der Parameter wird bereits in der umgewandelten Form geschrieben, so dass wir z.B. im Falle eines Presenters
<presenter=ProductEdit>
anstelle von <presenter=product-edit>
.
Allgemeine Filter
Neben Filtern für bestimmte Parameter können Sie auch allgemeine Filter definieren, die ein assoziatives Array mit allen
Parametern erhalten, die sie in beliebiger Weise ändern und dann zurückgeben können. Allgemeine Filter werden unter dem
Schlüssel null
definiert.
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 geben Ihnen die Möglichkeit, das Verhalten der Route in absolut beliebiger Weise anzupassen. Sie können z.
B. verwendet werden, um Parameter in Abhängigkeit von anderen Parametern zu ändern. Zum Beispiel, Übersetzung
<presenter>
und <action>
basierend auf dem aktuellen Wert des Parameters
<lang>
.
Wenn für einen Parameter ein benutzerdefinierter Filter definiert ist und gleichzeitig ein allgemeiner Filter existiert, wird
der benutzerdefinierte Filter FilterIn
vor dem allgemeinen Filter ausgeführt und umgekehrt wird der allgemeine
Filter FilterOut
vor dem benutzerdefinierten Filter ausgeführt. Innerhalb des allgemeinen Filters werden also die
Werte der Parameter presenter
bzw. action
im PascalCase- bzw. camelCase-Stil geschrieben.
Einweg-Flag
Einweg-Routen werden verwendet, um die Funktionalität alter URLs zu erhalten, die von der Anwendung nicht mehr generiert, aber
noch akzeptiert werden. Wir kennzeichnen sie mit 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 um, damit Suchmaschinen diese Seiten nicht doppelt indizieren (siehe SEO und Kanonisierung).
Dynamisches Routing mit Rückrufen
Dynamisches Routing mit Callbacks ermöglicht es Ihnen, den Routen direkt Funktionen (Callbacks) zuzuweisen, die ausgeführt werden, wenn der angegebene Pfad besucht wird. Diese flexible Funktion ermöglicht es Ihnen, schnell und effizient verschiedene Endpunkte für Ihre Anwendung zu erstellen:
$router->addRoute('test', function () {
echo 'You are at the /test address';
});
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' => 'Welcome to the Czech version of our website!',
'en' => 'Welcome to the English version of our website!',
};
});
Module
Wenn wir mehrere Routen haben, die zu einem Modul gehören, können
wir withModule()
verwenden, um sie zu gruppieren:
$router = new RouteList;
$router->withModule('Forum') // die folgenden Router sind Teil des Forum-Moduls
->addRoute('rss', 'Feed:rss') // Präsentator ist Forum:Feed
->addRoute('<presenter>/<action>')
->withModule('Admin') // die folgenden Router sind Teil des Moduls Forum:Admin
->addRoute('sign:in', 'Sign:in');
Eine Alternative ist die Verwendung des Parameters module
:
// Die URL manage/dashboard/default entspricht dem Präsentator Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
'module' => 'Admin',
]);
Subdomains
Routensammlungen können nach Subdomänen gruppiert werden:
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
Sie können auch Platzhalter in Ihrem Domänennamen verwenden:
$router = new RouteList;
$router->withDomain('example.%tld%')
// ...
Pfad-Präfix
Routensammlungen können nach dem Pfad in der URL gruppiert werden:
$router = new RouteList;
$router->withPath('eshop')
->addRoute('rss', 'Feed:rss') // entspricht der URL /eshop/rss
->addRoute('<presenter>/<action>'); // entspricht der URL /eshop/<Präsentator>/<Aktion>
Kombinationen
Die oben genannten Verwendungen können 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(/* ... */)
// ...
Abfrageparameter
Masken können auch Abfrageparameter (Parameter nach dem Fragezeichen in der URL) enthalten. Sie können keinen Validierungsausdruck definieren, aber sie können den Namen ändern, unter dem sie an den Präsentator übergeben werden:
// Abfrageparameter "cat" als "categoryId" in der Anwendung verwenden
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
Foo-Parameter
Wir gehen jetzt noch weiter in die Tiefe. Foo-Parameter sind im Grunde genommen unbenannte Parameter, die eine Übereinstimmung
mit einem regulären Ausdruck ermöglichen. Die folgende Route entspricht /index
, /index.html
,
/index.htm
und /index.php
:
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
Es ist auch möglich, explizit eine Zeichenkette zu definieren, die für die URL-Generierung verwendet werden soll. Die
Zeichenkette muss direkt nach dem Fragezeichen stehen. Die folgende Route ähnelt der vorherigen, erzeugt aber
/index.html
statt /index
, da die Zeichenfolge .html
als “generierter Wert”
festgelegt ist.
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
Einbindung
Um unseren Router in die Anwendung einzubinden, müssen wir ihn dem DI-Container mitteilen. Am einfachsten ist es, die Fabrik
vorzubereiten, die das Router-Objekt erstellt, und der Container-Konfiguration mitzuteilen, dass sie es verwenden soll. Schreiben
wir also eine Methode für diesen Zweck 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;
}
}
Dann schreiben wir in configuration:
services:
- App\Core\RouterFactory::createRouter
Alle Abhängigkeiten, wie z. B. eine Datenbankverbindung usw., werden der Factory-Methode als Parameter über Autowiring übergeben:
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
SimpleRouter
Ein viel einfacherer Router als die Route Collection ist SimpleRouter. Er kann verwendet
werden, wenn kein bestimmtes URL-Format erforderlich ist, wenn mod_rewrite
(oder Alternativen) nicht verfügbar ist
oder wenn wir uns einfach noch nicht mit benutzerfreundlichen URLs befassen wollen.
Erzeugt Adressen in etwa dieser Form:
http://example.com/?presenter=Product&action=detail&id=123
Der Parameter des SimpleRouter
-Konstruktors ist ein Standard-Präsentator und eine Aktion, d.h. eine Aktion, die
ausgeführt wird, wenn wir z.B. http://example.com/
ohne zusätzliche Parameter öffnen.
// Standardmäßig wird der Präsentator 'Home' und die Aktion 'default' verwendet
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
Wir empfehlen, SimpleRouter direkt in der Konfiguration zu definieren:
services:
- Nette\Application\Routers\SimpleRouter('Home:default')
SEO und Kanonisierung
Das Framework verbessert die Suchmaschinenoptimierung (SEO), indem es die Duplizierung von Inhalten unter verschiedenen URLs
verhindert. Wenn mehrere Adressen auf ein und dasselbe Ziel verweisen, z. B. /index
und /index.html
,
legt das Framework die erste als primär (kanonisch) fest und leitet die anderen mit dem HTTP-Code 301 auf sie um. Auf diese
Weise werden die Seiten von den Suchmaschinen nicht doppelt indiziert und ihr Page Rank wird nicht beeinträchtigt. .
Dieser Vorgang wird Kanonisierung genannt. Die kanonische URL ist diejenige, die vom Router erzeugt wird, d. h. von der ersten übereinstimmenden Route in der Sammlung ohne das OneWay-Flag. Daher führen wir in der Sammlung primäre Routen zuerst auf.
Die Kanonisierung wird vom Präsentator durchgeführt, mehr dazu im Kapitel Kanonisierung.
HTTPS
Um das HTTPS-Protokoll verwenden zu können, muss es beim Hosting aktiviert und der Server konfiguriert werden.
Die Umleitung der gesamten Website auf HTTPS muss auf Serverebene erfolgen, zum Beispiel über die Datei .htaccess im Stammverzeichnis unserer Anwendung mit dem HTTP-Code 301. Die Einstellungen können je nach Hosting unterschiedlich sein 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 eine URL mit demselben Protokoll, mit dem die Seite geladen wurde, es muss also nichts weiter eingestellt werden.
Wenn wir jedoch ausnahmsweise verschiedene Routen unter verschiedenen Protokollen benötigen, werden wir dies in der Routenmaske angeben:
// Erzeugt eine HTTP-Adresse
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);
// Erzeugt eine HTTPS-Adresse
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
Router debuggen
Die Routing-Leiste, die in Tracy Bar angezeigt wird, ist ein nützliches Werkzeug, das eine Liste von Routen und auch die Parameter anzeigt, die der Router von der URL erhalten hat.
Der grüne Balken mit dem Symbol ✓ steht für die Route, die mit der aktuellen URL übereinstimmt, die blauen Balken mit den Symbolen ≈ zeigen die Routen an, die ebenfalls mit der URL übereinstimmen würden, wenn Grün sie nicht überholen würde. Wir sehen den aktuellen Präsentator & Aktion weiter.
Gleichzeitig ist es bei einer unerwarteten Umleitung aufgrund einer Kanonisierung nützlich, in der Redirect-Leiste nachzusehen, wie der Router die URL ursprünglich verstanden hat und warum er sie umgeleitet hat.
Bei der Fehlersuche im Router empfiehlt es sich, die Entwicklertools im Browser zu öffnen (Strg+Umschalt+I oder Cmd+Option+I) und den Cache im Netzwerk-Panel zu deaktivieren, damit Umleitungen nicht darin gespeichert werden.
Leistung
Die Anzahl der Routen wirkt sich auf die Geschwindigkeit des Routers aus. Ihre Anzahl sollte auf keinen Fall einige Dutzend überschreiten. Wenn Ihre Website eine übermäßig komplizierte URL-Struktur hat, können Sie einen eigenen Router schreiben.
Wenn der Router keine Abhängigkeiten hat, z. B. von einer Datenbank, und seine Factory keine Argumente hat, können wir seine kompilierte Form direkt in einen DI-Container serialisieren und so die Anwendung etwas schneller machen.
routing:
cache: true
Benutzerdefinierter Router
Die folgenden Zeilen sind für sehr fortgeschrittene Benutzer gedacht. Sie können Ihren eigenen Router erstellen und ihn natürlich in Ihre Routensammlung aufnehmen. Der Router ist eine Implementierung der Schnittstelle 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 den aktuellen $httpRequest, aus
dem nicht nur die URL, sondern auch Header etc. abgerufen werden können, in ein Array, das den Presenter-Namen und seine
Parameter enthält. Kann sie die Anfrage nicht verarbeiten, gibt sie null zurück. Bei der Verarbeitung der Anfrage müssen wir
zumindest den Präsentator und die Aktion zurückgeben. Der Name des Präsentators ist vollständig und enthält alle Module:
[
'presenter' => 'Front:Home',
'action' => 'default',
]
Die Methode constructUrl
hingegen generiert eine absolute URL aus dem Array der Parameter. Sie kann die
Informationen aus dem Parameter $refUrl
verwenden, der die aktuelle URL ist.
Um der Routensammlung einen benutzerdefinierten Router hinzuzufügen, verwenden Sie add()
:
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
Getrennte Verwendung
Unter getrennter Nutzung verstehen wir die Verwendung der Router-Funktionen in einer Anwendung, die Nette Application und Presenter nicht verwendet. Fast alles, was wir in diesem Kapitel gezeigt haben, trifft auf sie zu, mit den folgenden Unterschieden:
- für Routensammlungen verwenden wir die Klasse Nette\Routing\RouteList
- als einfache Router-Klasse Nette\Routing\SimpleRouter
- da es kein Paar
Presenter:action
gibt, verwenden wir die Advanced-Notation
Wir werden also wieder eine Methode erstellen, die einen Router aufbaut, zum Beispiel:
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 Sie die Methode erneut zur Konfiguration hinzu und holen Sie 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 müssen wir den Router arbeiten lassen:
$params = $router->match($httpRequest);
if ($params === null) {
// keine passende Route gefunden, wir senden einen 404-Fehler
exit;
}
// wir verarbeiten die empfangenen Parameter
$controller = $params['controller'];
// ...
Umgekehrt werden wir den Router benutzen, um die Verbindung herzustellen:
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());