Routing
Router zajmuje się wszystkim wokół adresów URL, więc nie musisz już o nich myśleć. Zobaczmy:
- jak skonfigurować router, aby adresy URL były takie, jak chcesz.
- porozmawiamy o SEO i przekierowaniu
- a my pokażemy ci, jak napisać własny router
Bardziej ludzkie adresy URL (lub fajne lub ładne adresy URL) są bardziej użyteczne, bardziej zapamiętywane i przyczyniają się pozytywnie do SEO. Nette ma to na uwadze i w pełni odpowiada na potrzeby deweloperów. Możesz zaprojektować dokładnie taką strukturę adresu URL, jaką chcesz dla swojej aplikacji. Możesz nawet zaprojektować go po tym, jak aplikacja jest gotowa, ponieważ można to zrobić bez interwencji kodu lub szablonu. Dzieje się tak dlatego, że jest on zdefiniowany w elegancki sposób w jednym miejscu, w routerze, a więc nie jest rozproszony w postaci adnotacji we wszystkich prezenterach.
Router w Nette jest wyjątkowy, ponieważ jest dwukierunkowy. Może zarówno dekodować adresy URL w żądaniu HTTP, jak i tworzyć linki. Odgrywa więc istotną rolę w aplikacji Nette, ponieważ decyduje, który prezenter i akcja wykonają bieżące żądanie, ale jest również używany do generowania adresów URL w szablonie itp.
Jednak router nie jest ograniczony do tego zastosowania, możesz go użyć w aplikacjach, w których prezentery nie są w ogóle używane, dla interfejsów API REST itp. Więcej szczegółów można znaleźć w sekcji osobnego przypadku użycia.
Kolekcja routerów
Najwygodniejszy sposób definiowania postaci adresów URL w aplikacji zapewnia klasa Nette\Application\Routers\RouteList Definicja składa się z listy tzw. rout, czyli masek adresów URL oraz powiązanych z nimi prezenterów i akcji wykorzystujących proste API. Nie musimy w żaden sposób nazywać tras.
$router = new Nette\Application\Routers\RouteList;
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('article/<id>', 'Article:view');
// ...
Przykład mówi, że jeśli otworzymy w przeglądarce stronę https://domain.com/rss.xml
zostanie wyświetlony
z akcją rss
, jeśli https://domain.com/article/12
zostanie wyświetlony z akcją view
,
itd. Jeśli odpowiednia trasa nie zostanie znaleziona, aplikacja Nette odpowiada rzuceniem wyjątku BadRequestException, który
jest wyświetlany użytkownikowi jako strona błędu 404 Not Found.
Kolejność tras
Kolejność, w jakiej trasy są wymienione, jest całkowicie kluczowa, ponieważ są one oceniane kolejno od góry do dołu. Zasada jest taka, że deklarujemy trasy **od konkretnej do ogólnej:
// BŁĄD: 'rss.xml' łapie pierwszą rutę i traktuje ten ciąg jako <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');
// OK
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');
Trasy są również oceniane od góry do dołu podczas generowania linków:
// ŠPATNĚ: odkaz na 'Feed:rss' vygeneruje jako 'admin/feed/rss'
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');
// DOBŘE
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
Nie będziemy ukrywać przed Tobą, że prawidłowe trasowanie wymaga pewnych umiejętności. Dopóki nie wejdziesz w to, panel routingu będzie przydatnym narzędziem.
Maska i parametry
Maska opisuje względną ścieżkę od katalogu głównego witryny. Najprostszą maską jest statyczny adres URL:
$router->addRoute('products', 'Products:default');
Często maski zawierają tak zwane parametry. Są one wymienione w nawiasach spiczastych (np. <year>
)
i są przekazywane do docelowego prezentera, na przykład do metody renderShow(int $year)
lub do trwałego parametru
$year
:
$router->addRoute('chronicle/<year>', 'History:show');
Przykład mówi, że jeśli otworzymy stronę https://example.com/chronicle/2020
z akcją show
i parametrem year: 2020
.
Możemy określić domyślną wartość parametrów bezpośrednio w masce, czyniąc je opcjonalnymi:
$router->addRoute('chronicle/<year=2020>', 'History:show');
Trasa będzie teraz akceptować również adres URL https://example.com/chronicle/
z parametrem
year: 2020
.
Oczywiście parametrem może być również prezenter i nazwa wydarzenia. Na przykład:
$router->addRoute('<presenter>/<action>', 'Home:default');
Podana trasa przyjmuje np. adresy URL o postaci /article/edit
lub również /catalog/list
i rozumie
je jako prezentery i wydarzenia Article:edit
i Catalog:list
.
Jednocześnie nadaje parametrom presenter
i action
wartości domyślne Home
i
default
, a zatem są one również opcjonalne. Tak więc router akceptuje również adresy URL w postaci
/article
i traktuje je jako Article:default
. Lub odwrotnie, link do Product:default
wygeneruje ścieżkę /product
, link do domyślnego Home:default
wygeneruje ścieżkę
/
.
Maska może opisywać nie tylko ścieżkę względną z korzenia strony, ale także ścieżkę bezwzględną, jeśli zaczyna się od ukośnika, a nawet pełny bezwzględny adres URL, jeśli zaczyna się od dwóch ukośników:
// w stosunku do głównej części dokumentu
$router->addRoute('<presenter>/<action>', /* ... */);
// ścieżka bezwzględna (względem domeny)
$router->addRoute('/<presenter>/<action>', /* ... */);
// bezwzględny adres URL zawierający domenę (względny w stosunku do schematu)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);
// bezwzględny adres URL z uwzględnieniem schematu
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);
Wyrażenia walidacyjne
Warunek walidacji może być określony dla każdego parametru przy użyciu wyrażenia regularnego. Na przykład określamy, że
parametr id
może przyjmować tylko cyfry za pomocą wyrażenia regularnego \d+
:
$router->addRoute('<presenter>/<action>[/<id \d+>]', /* ... */);
Domyślnym wyrażeniem regularnym dla wszystkich parametrów jest [^/]+
, czyli wszystko poza ukośnikiem. Jeśli
parametr ma akceptować ukośniki, to podajemy wyrażenie .+
:
// akceptuje https://example.com/a/b/c, path bude 'a/b/c'
$router->addRoute('<path .+>', /* ... */);
Sekwencje opcjonalne
Części opcjonalne w masce można zaznaczyć za pomocą nawiasów kwadratowych. Każda część maski może być opcjonalna, można też dołączyć parametry:
$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);
// Akceptuje cesty:
// /en/download => lang => cs, name => download
// /download => lang => null, name => download
Kiedy parametr jest częścią sekwencji opcjonalnej, naturalnie staje się również opcjonalny. Jeśli nie ma wartości domyślnej, będzie to wartość null.
Opcjonalne części mogą być również w domenie:
$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);
Sekwencje mogą być dowolnie osadzane i łączone:
$router->addRoute(
'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
'Home:default',
);
// Akceptuje cesty:
// /en/hello
// /en-us/hello
// /hello
// /hello/page-12
Podczas generowania adresów URL poszukiwany jest najkrótszy wariant, więc wszystko, co można pominąć, jest pomijane.
Dlatego też np. routa index[.html]
generuje ścieżkę /index
. Możliwe jest odwrócenie zachowania
poprzez umieszczenie wykrzyknika po lewym nawiasie kwadratowym:
// akceptuje /hello i /hello.html, generuje /hello
$router->addRoute('<name>[.html]', /* ... */);
// akceptuje /hello i /hello.html, generuje /hello.html
$router->addRoute('<name>[!.html]', /* ... */);
Parametry opcjonalne (tzn. parametry posiadające wartość domyślną) bez nawiasów kwadratowych zachowują się zasadniczo tak, jakby były objęte nawiasami, jak poniżej:
$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);
// odpovídá tomuto:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);
Jeśli chcemy wpłynąć na zachowanie ukośnika spiczastego tak, aby np. zamiast /home/
generowany był tylko
/home
, można to zrobić w następujący sposób:
$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);
Żbiki
Możesz użyć następujących symboli wieloznacznych w masce ścieżki absolutnej, aby uniknąć, na przykład, konieczności wpisania domeny do maski, która może się różnić w środowiskach deweloperskich i produkcyjnych:
%tld%
= domena najwyższego poziomu, np.com
luborg
%sld%
= domena drugiego poziomu, np.example
%domain%
= domena bez subdomen, np.example.com
%host%
= cały host, np.www.example.com
%basePath%
= ścieżka do katalogu głównego
$router->addRoute('//www.%domain%/%basePath%/<presenter>/<action>', /* ... */);
$router->addRoute('//www.%sld%.%tld%/%basePath%/<presenter>/<action', /* ... */);
Wpis rozszerzony
Cel trasy, zwykle zapisany w postaci Presenter:action
, może być również wyrażony za pomocą tablicy, która
definiuje poszczególne parametry i ich wartości domyślne:
$router->addRoute('<presenter>/<action>[/<id \d+>]', [
'presenter' => 'Home',
'action' => 'default',
]);
Aby uzyskać bardziej szczegółową specyfikację, można użyć jeszcze bardziej rozszerzonej formy, w której oprócz
wartości domyślnych można ustawić inne właściwości parametru, takie jak wyrażenie regularne walidacji (patrz parametr
id
):
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>[/<id>]', [
'presenter' => [
Route::Value => 'Home',
],
'action' => [
Route::Value => 'default',
],
'id' => [
Route::Pattern => '\d+',
],
]);
Ważne jest, aby pamiętać, że jeśli parametry zdefiniowane w tablicy nie są zawarte w masce ścieżki, ich wartości nie mogą zostać zmienione, nawet przy użyciu parametrów zapytania określonych po znaku zapytania w adresie URL.
Filtry i tłumaczenia
Kod źródłowy aplikacji piszemy w języku angielskim, ale jeśli strona ma mieć czeski adres URL, to prosty typ routingu:
$router->addRoute('<presenter>/<action>', 'Home:default');
wygeneruje angielski adres URL, taki jak /product/123
lub /cart
. Jeśli chcemy, aby prezentery
i akcje w adresie URL były reprezentowane przez czeskie słowa (np. /produkt/123
lub /kosik
), możemy
użyć słownika tłumaczeń. Aby go napisać, potrzebujemy bardziej „verbose“ wersji drugiego parametru:
use Nette\Routing\Route;
$router->addRoute('<presenter>/<action>', [
'presenter' => [
Route::Value => 'Home',
Route::FilterTable => [
// řetězec v URL => presenter
'produkt' => 'Product',
'kosik' => 'Cart',
'katalog' => 'Catalog',
],
],
'action' => [
Route::Value => 'default',
Route::FilterTable => [
'seznam' => 'list',
],
],
]);
Wiele kluczy słownika tłumaczeń może prowadzić do tego samego prezentera. Dzięki temu zostaną utworzone różne aliasy do niego. Ostatni klucz jest traktowany jako wariant kanoniczny (czyli ten, który znajdzie się w wygenerowanym adresie URL).
Tablicę translacji można w ten sposób zastosować do każdego parametru. Jeśli nie istnieje tłumaczenie, przyjmowana jest
wartość oryginalna. To zachowanie można zmienić, dodając Route::FilterStrict => true
, a następnie router
odrzuci adres URL, jeśli wartość nie znajduje się w słowniku.
Oprócz słownika tłumaczeń w postaci tablicy można wdrożyć niestandardowe funkcje tłumaczeniowe.
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,
]);
Funkcja Route::FilterIn
dokonuje translacji pomiędzy parametrem w adresie URL a ciągiem znaków, który
następnie jest przekazywany do prezentera, funkcja FilterOut
dokonuje konwersji w przeciwnym kierunku.
Parametry presenter
, action
i module
mają już predefiniowane filtry, które
konwertują pomiędzy stylami PascalCase i camelCase używanymi w URL. Domyślna wartość parametrów jest już zapisana w
formie przekształconej, więc np. w przypadku prezentera piszemy <presenter=ProductEdit>
nie
<presenter=product-edit>
.
Filtry ogólne
Oprócz filtrów specyficznych dla parametrów, możemy również zdefiniować filtry ogólne, które otrzymują tablicę
asocjacyjną wszystkich parametrów, które mogą w dowolny sposób modyfikować, a następnie zwracają je. Definiujemy ogólne
filtry pod kluczem 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 { /* ... */ },
],
]);
Filtry generyczne dają możliwość modyfikowania zachowania routera w absolutnie dowolny sposób. Możemy na przykład użyć
ich do modyfikacji parametrów na podstawie innych parametrów. Na przykład, tłumaczenie <presenter>
a
<action>
na podstawie aktualnej wartości parametru <lang>
.
Jeśli parametr ma zdefiniowany filtr niestandardowy i jednocześnie istnieje filtr ogólny, niestandardowy
FilterIn
jest wykonywany przed ogólnym i odwrotnie, ogólny FilterOut
jest wykonywany przed
niestandardowym. Tak więc wewnątrz filtra ogólnego wartości parametrów presenter
i action
zapisywane są odpowiednio w stylu PascalCase i camelCase.
OneWay
Jednokierunkowe trasy są używane do zachowania funkcjonalności starych adresów URL, których aplikacja już nie generuje,
ale nadal akceptuje. Oznaczamy je flagą OneWay
:
// staré URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nové URL /product/123
$router->addRoute('product/<id>', 'Product:detail');
Kiedy użytkownik uzyskuje dostęp do starego adresu URL, prezenter automatycznie przekierowuje na nowy adres URL, aby wyszukiwarki nie indeksowały tych stron podwójnie (patrz SEO i kanoniczność).
Routing dynamiczny z wywołaniami zwrotnymi
Dynamiczny routing z wywołaniami zwrotnymi umożliwia bezpośrednie przypisywanie funkcji (wywołań zwrotnych) do tras, które zostaną wykonane po odwiedzeniu określonej ścieżki. Ta elastyczna funkcja umożliwia szybkie i wydajne tworzenie różnych punktów końcowych dla aplikacji:
$router->addRoute('test', function () {
echo 'You are at the /test address';
});
Można również zdefiniować parametry w masce, które będą automatycznie przekazywane do wywołania zwrotnego:
$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!',
};
});
Moduły
Jeśli mamy wiele tras, które wpadają do wspólnego modułu, używamy
withModule()
:
$router = new RouteList;
$router->withModule('Forum') // następujące trasy są częścią modułu Forum
->addRoute('rss', 'Feed:rss') // prezenterem będzie Forum:Feed
->addRoute('<presenter>/<action>')
->withModule('Admin') // następujące trasy są częścią modułu Forum:Admin
->addRoute('sign:in', 'Sign:in');
Alternatywą jest użycie parametru module
:
// URL manage/dashboard/default mapuje na prezenter Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
'module' => 'Admin',
]);
Subdomeny
Kolekcje tras mogą być podzielone na subdomeny:
$router = new RouteList;
$router->withDomain('example.com')
->addRoute('rss', 'Feed:rss')
->addRoute('<presenter>/<action>');
W nazwie domeny można również stosować symbole wieloznaczne:
$router = new RouteList;
$router->withDomain('example.%tld%')
// ...
Prefiks ścieżki
Kolekcje tras mogą być podzielone według ścieżki w adresie URL:
$router = new RouteList;
$router->withPath('eshop')
->addRoute('rss', 'Feed:rss') // chytry URL /eshop/rss
->addRoute('<presenter>/<action>'); // chytry URL /eshop/<presenter>/<action>
Kombinacja
Powyższe podziały można ze sobą łączyć:
$router = (new RouteList)
->withDomain('admin.example.com')
->withModule('Admin')
->addRoute(/* ... */)
->addRoute(/* ... */)
->end()
->withModule('Images')
->addRoute(/* ... */)
->end()
->end()
->withDomain('example.com')
->withPath('export')
->addRoute(/* ... */)
// ...
Parametry zapytań
Maski mogą również zawierać parametry zapytania (parametry po znaku zapytania w adresie URL). Nie można zdefiniować dla nich wyrażenia walidacyjnego, ale można zmienić nazwę, pod którą są przekazywane do prezentera:
// parametr zapytania 'cat', który chcemy wykorzystać w aplikacji pod nazwą 'categoryId'
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);
Parametry Foo
Teraz wchodzimy głębiej. Parametry Foo są w zasadzie nienazwanymi parametrami, które pozwalają na dopasowanie wyrażenia
regularnego. Przykładem jest rutyna, która akceptuje /index
, /index.html
, /index.htm
, i
/index.php
:
$router->addRoute('index<? \.html?|\.php|>', /* ... */);
Możesz również jawnie określić ciąg znaków, który ma być użyty podczas generowania adresu URL. Ciąg musi być
umieszczony bezpośrednio po znaku zapytania. Poniższa procedura jest podobna do poprzedniej, ale generuje
/index.html
zamiast /index
, ponieważ łańcuch .html
jest ustawiony jako wartość
generowania:
$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);
Integracja aplikacji
Aby zintegrować stworzony router z aplikacją, musimy powiedzieć o nim kontenerowi DI. Najprostszym sposobem na to jest
przygotowanie fabryki tworzącej obiekt routera i powiedzenie konfiguracji kontenera, aby go użyć. Załóżmy, że w tym celu
napiszemy metodę 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;
}
}
Następnie wpisujemy do konfiguracji:
services:
- App\Core\RouterFactory::createRouter
Wszelkie zależności, na przykład od bazy danych itp, są przekazywane do metody fabrycznej jako jej parametry przez autowiring:
public static function createRouter(Nette\Database\Connection $db): RouteList
{
// ...
}
SimpleRouter
Znacznie prostszym routerem od kolekcji rout jest SimpleRouter. Używamy go, jeśli nie
mamy specjalnych wymagań co do kształtu adresu URL, jeśli mod_rewrite
(lub jego alternatywy) nie jest dostępny,
lub jeśli nie chcemy jeszcze zajmować się ładnymi adresami URL.
Generuje on adresy URL w mniej więcej następującej formie:
http://example.com/?presenter=Product&action=detail&id=123
Parametrem konstruktora SimpleRouter jest domyślny prezenter & akcja, do której zostaniemy skierowani, jeśli otworzymy
stronę bez parametrów, np. http://example.com/
.
// výchozím presenterem bude 'Home' a akce 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');
Zalecane jest zdefiniowanie SimpleRoutera bezpośrednio w konfiguracji:
services:
- Nette\Application\Routers\SimpleRouter('Home:default')
SEO i kanonizacja
Framework przyczynia się do SEO (optymalizacja możliwości znalezienia w Internecie) poprzez zapobieganie duplikacji treści
w różnych adresach URL. Jeśli wiele adresów URL prowadzi do określonego miejsca docelowego, np. /index
i
/index.html
, framework wyznacza pierwszy z nich jako główny (kanoniczny) adres URL i przekierowuje do niego
pozostałe przy użyciu kodu HTTP 301. W ten sposób wyszukiwarki nie będą podwójnie indeksować Twojej witryny
i rozcieńczać jej page rank.
Proces ten nazywany jest kanonikalizacją. Kanoniczny adres URL to ten wygenerowany przez router, czyli pierwszy pasujący router w kolekcji bez flagi OneWay. Dlatego w kolekcji wymieniamy pierwsze trasy.
Kanonizacja jest wykonywana przez prezentera, zobacz rozdział Kanonizacja, aby uzyskać więcej szczegółów.
HTTPS
Aby korzystać z protokołu HTTPS, należy włączyć go na swoim hostingu i poprawnie skonfigurować swój serwer.
Przekierowanie całej strony na HTTPS należy ustawić na poziomie serwera, na przykład za pomocą pliku .htaccess w katalogu głównym naszej aplikacji, z kodem HTTP 301. Ustawienia mogą się różnić w zależności od hostingu i wyglądają coś takiego:
<IfModule mod_rewrite.c>
RewriteEngine On
...
RewriteCond %{HTTPS} off
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
...
</IfModule>
Router generuje adres URL z tym samym protokołem, z którym strona została załadowana, więc nie ma potrzeby ustawiania niczego innego.
Jeśli jednak wyjątkowo potrzebujemy, aby różne routery działały pod różnymi protokołami, określamy to w masce routera:
// Bude generovat adresu s HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);
// Bude generovat adresu s HTTPs
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);
Debugowanie routera
Pasek routingu wyświetlany w Tracy Bar to przydatne narzędzie, które wyświetla listę tras, a także parametry, które router uzyskał z adresu URL.
Zielony pasek z symbolem ✓ reprezentuje router, który przetworzył bieżący adres URL, natomiast niebieski pasek i symbol ≈ wskazują routery, które również przetworzyłyby adres URL, gdyby zielony pasek ich nie wyprzedził. Następnie widzimy aktualnego prezentera & akcję.
Jednocześnie, jeśli nastąpi nieoczekiwane przekierowanie z powodu kanonizacji, warto zajrzeć do paska redirect, aby zobaczyć, jak router pierwotnie zrozumiał adres URL i dlaczego dokonał przekierowania.
Podczas debugowania routera zalecamy otwarcie Developer Tools w przeglądarce (Ctrl+Shift+I lub Cmd+Option+I) i wyłączenie pamięci podręcznej w panelu Network, aby nie były w niej zapisywane przekierowania.
Wydajność
Liczba tras wpływa na szybkość działania routera. Liczba ta zdecydowanie nie powinna przekraczać kilkudziesięciu. Jeśli Twoja witryna ma zbyt skomplikowaną strukturę adresów URL, możesz napisać własny niestandardowy router.
Jeśli router nie ma żadnych zależności, na przykład od bazy danych, a jego fabryka nie przyjmuje żadnych argumentów, możemy serializować jego skompilowaną postać bezpośrednio do kontenera DI i w ten sposób nieco przyspieszyć działanie aplikacji.
routing:
cache: true
Router na zamówienie
Poniższe linie przeznaczone są dla bardzo zaawansowanych użytkowników. Możesz stworzyć własny router i naturalnie zintegrować go ze swoją kolekcją routingu. Router jest implementacją interfejsu Nette\Routing\Router z dwoma metodami:
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
{
// ...
}
}
Metoda match
przetwarza bieżące żądanie $httpRequest, z którego można uzyskać
nie tylko adres URL, ale także nagłówki itp. w tablicy zawierającej nazwę prezentera i jego parametry. Jeśli nie może
przetworzyć żądania, zwróci null. Podczas przetwarzania żądania musimy zwrócić co najmniej prezentera i akcję. Nazwa
prezentera jest kompletna i zawiera wszelkie moduły:
[
'presenter' => 'Front:Home',
'action' => 'default',
]
Metoda constructUrl
, z drugiej strony, konstruuje wynikowy bezwzględny adres URL z tablicy parametrów. Aby to
zrobić, może użyć informacji z tablicy $refUrl
, czyli aktualny adres URL.
Aby dodać go do kolekcji rout, użyj add()
:
$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...
Użytkowanie samodzielne
Przez użycie samodzielne rozumiemy wykorzystanie możliwości routera w aplikacji, która nie korzysta z Nette Application i prezenterów. Dotyczy go prawie wszystko, co pokazaliśmy w tym rozdziale, z następującymi różnicami:
- dla kolekcji rutynowych używamy klasy Nette\Routing\RouteList
- jako klasę prostego routera Nette\Routing\SimpleRouter
- ponieważ nie istnieje para
Presenter:action
, używamy rozszerzonej notacji
Czyli znowu tworzymy metodę, która buduje dla nas np. router:
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;
}
}
Jeśli używasz kontenera DI, który zalecamy, ponownie dodajemy metodę do konfiguracji, a następnie otrzymujemy router wraz z żądaniem HTTP z kontenera:
$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);
Możemy też wykonać obiekty bezpośrednio:
$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();
Teraz pozostaje nam tylko doprowadzić router do stanu używalności:
$params = $router->match($httpRequest);
if ($params === null) {
// nie znaleziono pasującego routera, wyślij błąd 404
exit;
}
// przetwarzanie uzyskanych parametrów
$controller = $params['controller'];
// ...
I w odwrotnym kierunku, wykorzystujemy router do budowy łącza:
$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());