Routowanie

Router odpowiada za wszystko związane z adresami URL, abyś Ty już nie musiał się nad nimi zastanawiać. Pokażemy:

  • jak ustawić router, aby URL były zgodne z oczekiwaniami
  • powiemy o SEO i przekierowaniach
  • i pokażemy, jak napisać własny router

Bardziej ludzkie URL (lub też cool czy pretty URL) są bardziej użyteczne, łatwiejsze do zapamiętania i pozytywnie wpływają na SEO. Nette o tym myśli i w pełni wychodzi naprzeciw deweloperom. Możesz dla swojej aplikacji zaprojektować dokładnie taką strukturę adresów URL, jaką będziesz chciał. Możesz ją zaprojektować nawet w chwili, gdy aplikacja jest już gotowa, ponieważ obejdzie się to bez ingerencji w kod czy szablony. Definiuje się ją bowiem w elegancki sposób w jednym jedynym miejscu, w routerze, i nie jest rozproszona w formie adnotacji we wszystkich presenterach.

Router w Nette jest wyjątkowy tym, że jest dwukierunkowy. Potrafi zarówno dekodować URL w żądaniu HTTP, jak i tworzyć linki. Odgrywa więc kluczową rolę w Nette Application, ponieważ nie tylko decyduje o tym, który presenter i akcja będzie wykonywać aktualne żądanie, ale także wykorzystuje się go do generowania URL w szablonie itp.

Jednak router nie jest ograniczony tylko do tego zastosowania, można go używać w aplikacjach, gdzie w ogóle nie używa się presenterów, dla REST API, itd. Więcej w części Samostatné použití.

Kolekcja tras

Najprzyjemniejszy sposób definiowania postaci adresów URL w aplikacji oferuje klasa Nette\Application\Routers\RouteList. Definicja składa się z listy tzw. tras (routes), czyli masek adresów URL i przypisanych do nich presenterów i akcji za pomocą prostego API. Tras nie musimy w żaden sposób nazywać.

$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 w przeglądarce otworzymy https://domain.com/rss.xml, wyświetli się presenter Feed z akcją rss, jeśli https://domain.com/article/12, wyświetli się presenter Article z akcją view itd. W przypadku nieznalezienia odpowiedniej trasy Nette Application reaguje wyrzuceniem wyjątku BadRequestException, który użytkownikowi wyświetli się jako strona błędu 404 Not Found.

Kolejność tras

Absolutnie kluczowa jest kolejność, w jakiej są wymienione poszczególne trasy, ponieważ są one ewaluowane kolejno od góry do dołu. Obowiązuje zasada, że trasy deklarujemy od szczegółowych do ogólnych:

// ŹLE: 'rss.xml' przechwyci pierwsza trasa i rozumie ten ciąg jako <slug>
$router->addRoute('<slug>', 'Article:view');
$router->addRoute('rss.xml', 'Feed:rss');

// DOBRZE
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('<slug>', 'Article:view');

Trasy są ewaluowane od góry do dołu również przy generowaniu linków:

// ŹLE: link do 'Feed:rss' wygeneruje jako 'admin/feed/rss'
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');
$router->addRoute('rss.xml', 'Feed:rss');

// DOBRZE
$router->addRoute('rss.xml', 'Feed:rss');
$router->addRoute('admin/<presenter>/<action>', 'Admin:default');

Nie będziemy przed Tobą ukrywać, że prawidłowe zestawienie tras wymaga pewnej wprawy. Zanim ją opanujesz, użytecznym pomocnikiem będzie panel routingu.

Maska i parametry

Maska opisuje ścieżkę względną od katalogu głównego strony internetowej. Najprostszą maską jest statyczny URL:

$router->addRoute('products', 'Products:default');

Często maski zawierają tzw. parametry. Są one podane w nawiasach ostrych (np. <year>) i są przekazywane do docelowego presentera, 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 w przeglądarce otworzymy https://example.com/chronicle/2020, wyświetli się presenter History z akcją show i parametrem year: 2020.

Parametrom możemy określić wartość domyślną bezpośrednio w masce i tym samym stają się one opcjonalne:

$router->addRoute('chronicle/<year=2020>', 'History:show');

Trasa będzie teraz akceptować również URL https://example.com/chronicle/, które ponownie wyświetli History:show z parametrem year: 2020.

Parametrem może być oczywiście również nazwa presentera i akcji. Na przykład tak:

$router->addRoute('<presenter>/<action>', 'Home:default');

Podana trasa akceptuje np. URL w postaci /article/edit lub także /catalog/list i rozumie je jako presentery i akcje 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 trasa akceptuje również URL w postaci /article i rozumie go jako Article:default. Lub odwrotnie, link do Product:default wygeneruje ścieżkę /product, link do domyślnego Home:default ścieżkę /.

Maska może opisywać nie tylko ścieżkę względną od katalogu głównego strony internetowej, ale także ścieżkę absolutną, jeśli zaczyna się od ukośnika, lub nawet cały absolutny URL, jeśli zaczyna się od dwóch ukośników:

// względnie do document root
$router->addRoute('<presenter>/<action>', /* ... */);

// ścieżka absolutna (względna do domeny)
$router->addRoute('/<presenter>/<action>', /* ... */);

// absolutny URL włącznie z domeną (względny do schematu)
$router->addRoute('//<lang>.example.com/<presenter>/<action>', /* ... */);

// absolutny URL włącznie ze schematem
$router->addRoute('https://<lang>.example.com/<presenter>/<action>', /* ... */);

Wyrażenia walidacyjne

Dla każdego parametru można ustalić warunek walidacyjny za pomocą wyrażenia regularnego. Na przykład dla parametru id określimy, że 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 [^/]+, tj. wszystko oprócz ukośnika. Jeśli parametr ma akceptować również ukośniki, podamy wyrażenie .+:

// akceptuje https://example.com/a/b/c, path będzie 'a/b/c'
$router->addRoute('<path .+>', /* ... */);

Sekwencje opcjonalne

W masce można oznaczać opcjonalne części za pomocą nawiasów kwadratowych. Opcjonalna może być dowolna część maski, mogą się w niej znajdować również parametry:

$router->addRoute('[<lang [a-z]{2}>/]<name>', /* ... */);

// Akceptuje ścieżki:
//    /cs/download  => lang => cs, name => download
//    /download     => lang => null, name => download

Gdy parametr jest częścią sekwencji opcjonalnej, staje się oczywiście również opcjonalny. Jeśli nie ma podanej wartości domyślnej, będzie miał wartość null.

Opcjonalne części mogą być również w domenie:

$router->addRoute('//[<lang=en>.]example.com/<presenter>/<action>', /* ... */);

Sekwencje można dowolnie zagnieżdżać i kombinować:

$router->addRoute(
	'[<lang [a-z]{2}>[-<sublang>]/]<name>[/page-<page=0>]',
	'Home:default',
);

// Akceptuje ścieżki:
// 	/cs/hello
// 	/en-us/hello
// 	/hello
// 	/hello/page-12

Przy generowaniu URL dąży się do najkrótszej warianty, więc wszystko, co można pominąć, jest pomijane. Dlatego na przykład trasa index[.html] generuje ścieżkę /index. Odwrócić zachowanie można przez podanie wykrzyknika za lewym nawiasem 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 (tj. parametry mające wartość domyślną) bez nawiasów kwadratowych zachowują się w zasadzie tak, jakby były ujęte w nawiasy w następujący sposób:

$router->addRoute('<presenter=Home>/<action=default>/<id=>', /* ... */);

// odpowiada temu:
$router->addRoute('[<presenter=Home>/[<action=default>/[<id>]]]', /* ... */);

Jeśli chcielibyśmy wpłynąć na zachowanie końcowego ukośnika, aby np. zamiast /home/ generowało się tylko /home, można to osiągnąć w ten sposób:

$router->addRoute('[<presenter=Home>[/<action=default>[/<id>]]]', /* ... */);

Symbole wieloznaczne

W masce ścieżki absolutnej możemy użyć następujących symboli wieloznacznych i uniknąć w ten sposób np. konieczności zapisywania w masce domeny, która może się różnić w środowisku deweloperskim i produkcyjnym:

  • %tld% = top level domain, np. com lub org
  • %sld% = second level domain, 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', /* ... */);

Zapis rozszerzony

Cel trasy, zazwyczaj zapisywany w postaci Presenter:action, może być również zapisany 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',
]);

Dla bardziej szczegółowej specyfikacji można użyć jeszcze bardziej rozszerzonej formy, gdzie oprócz wartości domyślnych możemy ustawić również inne właściwości parametrów, takie jak na przykład walidacyjne wyrażenie regularne (zobacz 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 zauważenie, że jeśli parametry zdefiniowane w tablicy nie są wymienione w masce ścieżki, ich wartości nie można zmienić, nawet za pomocą parametrów query podanych za znakiem zapytania w URL.

Filtry i tłumaczenia

Kody źródłowe aplikacji piszemy w języku angielskim, ale jeśli strona ma mieć polskie URL, to proste routowanie typu:

$router->addRoute('<presenter>/<action>', 'Home:default');

będzie generować angielskie URL, takie jak /product/123 lub /cart. Jeśli chcemy mieć presentery i akcje w URL reprezentowane polskimi słowami (np. /produkt/123 lub /koszyk), możemy wykorzystać słownik tłumaczeń. Do jego zapisu potrzebujemy już “bardziej gadatliwej” warianty drugiego parametru:

use Nette\Routing\Route;

$router->addRoute('<presenter>/<action>', [
	'presenter' => [
		Route::Value => 'Home',
		Route::FilterTable => [
			// ciąg w URL => presenter
			'produkt' => 'Product',
			'koszyk' => 'Cart',
			'katalog' => 'Catalog',
		],
	],
	'action' => [
		Route::Value => 'default',
		Route::FilterTable => [
			'lista' => 'list',
		],
	],
]);

Wiele kluczy słownika tłumaczeń może prowadzić do tego samego presentera. W ten sposób tworzy się dla niego różne aliasy. Za wariant kanoniczny (czyli ten, który będzie w wygenerowanym URL) uważa się ostatni klucz.

Tabelę tłumaczeń można w ten sposób użyć dla dowolnego parametru. Przy czym jeśli tłumaczenie nie istnieje, bierze się pierwotną wartość. To zachowanie możemy zmienić dodając Route::FilterStrict => true i trasa wtedy odrzuci URL, jeśli wartość nie jest w słowniku.

Oprócz słownika tłumaczeń w postaci tablicy można zastosować również własne funkcje tłumaczące.

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 konwertuje między parametrem w URL a ciągiem, który następnie jest przekazywany do presentera, funkcja FilterOut zapewnia konwersję w przeciwnym kierunku.

Parametry presenter, action i module już mają predefiniowane filtry, które konwertują między stylem PascalCase resp. camelCase a kebab-case używanym w URL. Wartość domyślna parametrów zapisuje się już w przekształconej postaci, więc na przykład w przypadku presentera piszemy <presenter=ProductEdit>, a nie <presenter=product-edit>.

Filtry ogólne

Oprócz filtrów przeznaczonych dla konkretnych parametrów możemy zdefiniować również filtry ogólne, które otrzymają tablicę asocjacyjną wszystkich parametrów, które mogą dowolnie modyfikować, a następnie je zwrócą. Filtry ogólne definiujemy 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 ogólne dają możliwość modyfikacji zachowania trasy w absolutnie dowolny sposób. Możemy je użyć na przykład do modyfikacji parametrów na podstawie innych parametrów. Na przykład tłumaczenie <presenter> i <action> na podstawie aktualnej wartości parametru <lang>.

Jeśli parametr ma zdefiniowany własny filtr i jednocześnie istnieje filtr ogólny, wykonuje się własny FilterIn przed ogólnym i odwrotnie ogólny FilterOut przed własnym. Zatem wewnątrz filtra ogólnego wartości parametrów presenter resp. action są zapisane w stylu PascalCase resp. camelCase.

Jednokierunkowe OneWay

Trasy jednokierunkowe używa się do zachowania funkcjonalności starych URL, których aplikacja już nie generuje, ale nadal akceptuje. Oznaczamy je flagą OneWay:

// stare URL /product-info?id=123
$router->addRoute('product-info', 'Product:detail', $router::ONE_WAY);
// nowe URL /product/123
$router->addRoute('product/<id>', 'Product:detail');

Przy dostępie do starego URL presenter automatycznie przekierowuje na nowy URL, dzięki czemu wyszukiwarki nie zaindeksują tych stron dwukrotnie (zobacz SEO a kanonizace).

Dynamiczne routowanie z callbackami

Dynamiczne routowanie z callbackami pozwala przypisać trasom bezpośrednio funkcje (callbacki), które zostaną wykonane, gdy dana ścieżka zostanie odwiedzona. Ta elastyczna funkcjonalność pozwala szybko i efektywnie tworzyć różne punkty końcowe (endpoints) dla Twojej aplikacji:

$router->addRoute('test', function () {
	echo 'jesteś pod adresem /test';
});

Możesz również zdefiniować w masce parametry, które zostaną automatycznie przekazane do Twojego callbacku:

$router->addRoute('<lang cs|en>', function (string $lang) {
	echo match ($lang) {
		'cs' => 'Witaj na czeskiej wersji naszej strony!',
		'en' => 'Welcome to the English version of our website!',
	};
});

Moduły

Jeśli mamy więcej tras, które należą do wspólnego modułu, wykorzystamy withModule():

$router = new RouteList;
$router->withModule('Forum') // następujące trasy są częścią modułu Forum
	->addRoute('rss', 'Feed:rss') // presenter 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 się na presenter Admin:Dashboard
$router->addRoute('manage/<presenter>/<action>', [
	'module' => 'Admin',
]);

Subdomeny

Kolekcje tras możemy dzielić według subdomen:

$router = new RouteList;
$router->withDomain('example.com')
	->addRoute('rss', 'Feed:rss')
	->addRoute('<presenter>/<action>');

W nazwie domeny można użyć również Zástupné znaky:

$router = new RouteList;
$router->withDomain('example.%tld%')
	// ...

Prefiks ścieżki

Kolekcje tras możemy dzielić według ścieżki w URL:

$router = new RouteList;
$router->withPath('eshop')
	->addRoute('rss', 'Feed:rss') // łapie URL /eshop/rss
	->addRoute('<presenter>/<action>'); // łapie URL /eshop/<presenter>/<action>

Kombinacje

Powyższe podziały możemy wzajemnie kombinować:

$router = (new RouteList)
	->withDomain('admin.example.com')
		->withModule('Admin')
			->addRoute(/* ... */)
			->addRoute(/* ... */)
		->end()
		->withModule('Images')
			->addRoute(/* ... */)
		->end()
	->end()
	->withDomain('example.com')
		->withPath('export')
			->addRoute(/* ... */)
			// ...

Parametry Query

Maski mogą również zawierać parametry query (parametry za znakiem zapytania w URL). Nie można im zdefiniować wyrażenia walidacyjnego, ale można zmienić nazwę, pod którą zostaną przekazane do presentera:

// parametr query 'cat' chcemy w aplikacji użyć pod nazwą 'categoryId'
$router->addRoute('product ? id=<productId> & cat=<categoryId>', /* ... */);

Parametry Foo

Teraz już idziemy głębiej. Parametry Foo to w zasadzie nienazwane parametry, które umożliwiają dopasowanie wyrażenia regularnego. Przykładem jest trasa akceptująca /index, /index.html, /index.htm i /index.php:

$router->addRoute('index<? \.html?|\.php|>', /* ... */);

Można również jawnie zdefiniować ciąg, który będzie użyty przy generowaniu URL. Ciąg musi być umieszczony bezpośrednio za znakiem zapytania. Następująca trasa jest podobna do poprzedniej, ale generuje /index.html zamiast /index, ponieważ ciąg .html jest ustawiony jako wartość generująca:

$router->addRoute('index<?.html \.html?|\.php|>', /* ... */);

Włączenie do aplikacji

Aby włączyć utworzony router do aplikacji, musimy o nim powiedzieć kontenerowi DI. Najłatwiejszą drogą jest przygotowanie fabryki, która wyprodukuje obiekt routera, i poinformowanie w konfiguracji kontenera, że ma jej użyć. Powiedzmy, ż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;
	}
}

Do konfiguracji następnie zapiszemy:

services:
	- App\Core\RouterFactory::createRouter

Wszelkie zależności, na przykład od bazy danych itp., zostaną przekazane do metody fabrycznej jako jej parametry za pomocą autowiringu:

public static function createRouter(Nette\Database\Connection $db): RouteList
{
	// ...
}

SimpleRouter

Znacznie prostszym routerem niż kolekcja tras jest SimpleRouter. Użyjemy go wtedy, gdy nie mamy szczególnych wymagań co do kształtu URL, gdy nie jest dostępny mod_rewrite (lub jego alternatywy) lub gdy na razie nie chcemy zajmować się ładnymi URL.

Generuje adresy mniej więcej w tym kształcie:

http://example.com/?presenter=Product&action=detail&id=123

Parametrem konstruktora SimpleRoutera jest domyślny presenter & akcja, na który ma kierować, jeśli otworzymy stronę bez parametrów, np. http://example.com/.

// domyślnym presenterem będzie 'Home' a akcja 'default'
$router = new Nette\Application\Routers\SimpleRouter('Home:default');

Zalecamy SimpleRouter bezpośrednio zdefiniować w konfiguracji:

services:
	- Nette\Application\Routers\SimpleRouter('Home:default')

SEO i kanonizacja

Framework przyczynia się do SEO (optymalizacji dla wyszukiwarek internetowych) przez zapobieganie duplikacji treści pod różnymi URL. Jeśli do określonego celu prowadzi więcej adresów, np. /index i /index.html, framework pierwszy z nich określa jako podstawowy (kanoniczny) i pozostałe na niego przekierowuje za pomocą kodu HTTP 301. Dzięki temu wyszukiwarki nie indeksują stron dwukrotnie i nie rozdrabniają ich page rank.

Ten proces nazywa się kanonizacją. Kanonicznym URL jest ten, który generuje router, tj. pierwsza pasująca trasa w kolekcji bez flagi OneWay. Dlatego w kolekcji podajemy podstawowe trasy jako pierwsze.

Kanonizację przeprowadza presenter, więcej w rozdziale kanonizacja.

HTTPS

Aby móc używać protokołu HTTPS, konieczne jest jego włączenie na hostingu i prawidłowe skonfigurowanie serwera.

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, i to z kodem HTTP 301. Ustawienie może się różnić w zależności od hostingu i wygląda mniej więcej tak:

<IfModule mod_rewrite.c>
	RewriteEngine On
	...
	RewriteCond %{HTTPS} off
	RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
	...
</IfModule>

Router generuje URL z tym samym protokołem, z jakim została załadowana strona, więc nic więcej nie trzeba ustawiać.

Jeśli jednak wyjątkowo potrzebujemy, aby różne trasy działały pod różnymi protokołami, podamy go w masce trasy:

// Będzie generować adres z HTTP
$router->addRoute('http://%host%/<presenter>/<action>', /* ... */);

// Będzie generować adres z HTTPS
$router->addRoute('https://%host%/<presenter>/<action>', /* ... */);

Debugowanie routera

Panel routingu wyświetlający się w Tracy Bar jest użytecznym pomocnikiem, który wyświetla listę tras oraz parametrów, które router uzyskał z URL.

Zielony pasek z symbolem ✓ reprezentuje trasę, która przetworzyła aktualny URL, niebieskim kolorem i symbolem ≈ są oznaczone trasy, które również przetworzyłyby URL, gdyby zielona ich nie wyprzedziła. Dalej widzimy aktualny presenter & akcję.

Jednocześnie jeśli dojdzie do nieoczekiwanego przekierowania z powodu kanonizacji, warto spojrzeć do panelu w pasku redirect, gdzie dowiesz się, jak router pierwotnie zrozumiał URL i dlaczego przekierował.

Podczas debugowania routera zalecamy otwarcie w przeglądarce Developer Tools (Ctrl+Shift+I lub Cmd+Option+I) i w panelu Network wyłączenie cache, aby nie zapisywały się w niej przekierowania.

Wydajność

Liczba tras ma wpływ na szybkość routera. Ich liczba zdecydowanie nie powinna przekraczać kilkudziesięciu. Jeśli Twoja strona ma zbyt skomplikowaną strukturę URL, możesz napisać na miarę Vlastní 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 jego skompilowaną postać zserializować bezpośrednio do kontenera DI i tym samym nieznacznie przyspieszyć aplikację.

routing:
	cache: true

Własny router

Poniższe linijki są przeznaczone dla bardzo zaawansowanych użytkowników. Możesz stworzyć własny router i całkowicie naturalnie włączyć go do kolekcji tras. Router jest implementacją interfejsu Nette\Routing\Router z dwiema 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 aktualne żądanie $httpRequest, z którego można uzyskać nie tylko URL, ale i nagłówki itp., do tablicy zawierającej nazwę presentera i jego parametry. Jeśli nie potrafi przetworzyć żądania, zwraca null. Przy przetwarzaniu żądania musimy zwrócić co najmniej presenter i akcję. Nazwa presentera jest pełna i zawiera również ewentualne moduły:

[
	'presenter' => 'Front:Home',
	'action' => 'default',
]

Metoda constructUrl odwrotnie, składa z tablicy parametrów wynikowy absolutny URL. Do tego może wykorzystać informacje z parametru $refUrl, który jest aktualnym URL.

Do kolekcji tras dodasz go za pomocą add():

$router = new Nette\Application\Routers\RouteList;
$router->add($myRouter);
$router->addRoute(/* ... */);
// ...

Samostatné použití

Samodzielnym użyciem rozumiemy wykorzystanie możliwości routera w aplikacji, która nie wykorzystuje Nette Application i presenterów. Dotyczy go prawie wszystko, co pokazaliśmy w tym rozdziale, z tymi różnicami:

Więc ponownie tworzymy metodę, która nam zbuduje router, np.:

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, co zalecamy, ponownie dodamy metodę do konfiguracji, a następnie router wraz z żądaniem HTTP uzyskamy z kontenera:

$router = $container->getByType(Nette\Routing\Router::class);
$httpRequest = $container->getByType(Nette\Http\IRequest::class);

Albo obiekty bezpośrednio wyprodukujemy:

$router = App\Core\RouterFactory::createRouter();
$httpRequest = (new Nette\Http\RequestFactory)->fromGlobals();

Teraz już pozostaje puścić router do pracy:

$params = $router->match($httpRequest);
if ($params === null) {
	// nie znaleziono pasującej trasy, wysyłamy błąd 404
	exit;
}

// przetwarzamy uzyskane parametry
$controller = $params['controller'];
// ...

I odwrotnie użyjemy routera do zbudowania linku:

$params = ['controller' => 'ArticleController', 'id' => 123];
$url = $router->constructUrl($params, $httpRequest->getUrl());
wersja: 4.0