Router
V tuto chvíli již máte celkové povědomí o tom jak Jet MVC funguje, víte co je báze, co je stránka a kontroler. Jeden z posledních dílků co schází do úplnosti skládačky je router.
A hned na začátek jedna důležitá informace. Router hlavně vyhodnocuje. Na základě URL rozhodne o jaký požadavek se jedná (o jakou bázi,lokalizaci, stránku a jak bude naloženo s případným zbytkem URL) a nastaví celkový stav. Ale router již neprovádí nic jako posílání http hlaviček, generování výstupu a tak dále. Ne, to není práce pro router. Abych byl přesný, tak router musí provést věci, které jsou součástí resolv procesu - tedy zavolat inicializer báze, nastavit lokalizaci a překladač. To je nutná část vyhodnocovacího procesu. Ale již nepošle na výstup vygenerovanou stránku. To již routeru nepřísluší.
V praxi se děje to, že se v systému vytvoří singleton třídy Jet\MVC_Router (nebo jiné - vaší implementace, pokud to určíte pomocí továrny) a následně se vyhodnotí URL, kterou obdrží jako parametr (může tedy jít o jakoukoliv URL, ne pouze tu z aktuálního http požadavku).
Router provede analýzu / vyhodnocení této URL (zjistí o jakou bázi, lokalizaci a stránku se jedná) a nastaví svůj vnitřní stav a stav systému (například aktuální lokalizaci).
Až pak následuje proces, kdy se na základě zjištěných informací (vnitřního stavu routeru) skutečností provádí činnost, která fakticky znamená poslání odpovědi na http požadavek.
A i když činnost provedená na základě vyhodnocení není integrální součástí routeru, tak je to činnost na routeru přímo závislá a dobře to demonstruje co vše router vyhodnotí. Tento poslední krok najdete ve třídě Jet\Application, metodě runMVC().
Metoda Application::runMVC()
Možná jste se pozastavili nad tím co tu najednou dělá třída Jet\Application, když se bavíme o MVC. Jak bylo výše uvedeno, tak router pouze vyhodnocuje URL a nastavuje potřebný stav.
Ukažme si to na příkladu. Router zjistí zda se náhodou nejedná o přesměrování. Tedy zjistí, že daná URL je sice platná, ale má směřovat jinam (teď neřešme z jakého důvodu). Ale jak to přesměrování bude ve finále provedeno? Tedy kdo, co a jak pošle potřebné http hlavičky? A budou doopravdy http hlavičky odeslány? To už není věc MVC, ale čistě věc vaší aplikace. Ano, ve většině případů stačí poslat hlavičky a hotovo. Ale co když vám o přesměrování nejde, ale chcete třeba náhled stránky pro vaše CMS? Nebo co když je při přesměrování třeba provést nějakou operaci, kterou Jet sám o sobě neumí?
A další příklad: Co takhle stránka, která vyžaduje přihlášení a ověření práv. Jistě, v Jet existuje standardní způsob jak to obsloužit a ten si ukážeme dále. Ale opět nikde není psáno, že to ve vaší aplikaci musí být řešeno právě tak, jak to Jet dělá standardně. To by vás omezovalo a bralo vám to svobodu a manévrovací prostor.
Právě z těchto důvodů je činnost vykonávaná na základě zjištění routeru zcela separovaná od MVC a vlastně náleží do aplikačního prostoru. V Jet je k dispozici výchozí implementace obsloužení celé posloupnosti zpracování, která by měla být pro většinu situací dostačující - to je právě metoda Application::runMVC(), která je volána z ~/application/bootstrap.php. Ovšem uspořádání dává jasně najevo, že si můžete implementovat svou vlastní posloupnost a logiku konečného zpracování požadavku.
A pro názornost bude úplně nejlepší se na tuto jednoduchou metodu rovnou podívat:
public static function runMVC( ?string $URL = null ): void
{
Debug_Profiler::blockStart( 'MVC router - Init and resolve' );
$router = MVC::getRouter();
$router->resolve( $URL );
Debug_Profiler::blockEnd( 'MVC router - Init and resolve' );
if( $router->getIsRedirect() ) {
Http_Headers::redirect(
$router->getRedirectType(),
$router->getRedirectTargetURL()
);
}
if( $router->getHasUnusedUrlPath() ) {
Http_Headers::movedPermanently( $router->getValidUrl() );
}
if( $router->getIs404() ) {
ErrorPages::handleNotFound( false );
return;
}
$base = $router->getBase();
$locale = $router->getLocale()
$page = $router->getPage();
if( !$base->getIsActive() ) {
ErrorPages::handleServiceUnavailable( false );
return;
}
if( !$base->getLocalizedData( $locale )->getIsActive() ) {
ErrorPages::handleNotFound( false );
return;
}
if( !$page->getIsActive() ) {
ErrorPages::handleNotFound( false );
return;
}
if(
$page->getSSLRequired() &&
!Http_Request::isHttps()
) {
Http_Headers::movedPermanently( Http_Request::URL(
include_query_string: true,
force_SSL: true
) );
}
if( $router->getLoginRequired() ) {
Auth::handleLogin();
return;
}
if( $router->getAccessNotAllowed() ) {
ErrorPages::handleUnauthorized( false );
return;
}
$result = $page->render();
$page->handleHttpHeaders();
echo $result;
}
Z kódu by mělo být patrné jak konečné zpravování standardně probíhá. Ale pár důležitých věcí je třeba zdůraznit.
- Router je singleton, jehož instanci drží třída Jet\MVC. Tedy router nesmí být někde ve vzduchoprázdnu například jako globální proměnná a také musí vždy existovat právě jedna aktivní instance routeru. Jet stejně jako vaše aplikace s routerem pracuje - používá jej. Je to tedy klíčové.
- Je vidět, že na rozhodovací proces (resolv) je vytvořen blok pro profiler. Rozhodovací proces musí být rychlý. Ten blok je tedy velice důležitý.
- A s dovolením ještě jendou připomenu, že toto vše si můžete v aplikačním prostoru udělat jak chcete vy. Nehodí se se vám například výchozí strategie v situaci kdy je třeba jazyková mutace neaktivní? Vůbec nic vám nebrání si udělat svou posloupnost a svou vlastní metodu runMVC.
Ještě než se vrhneme na router samotný, tak je nutné si něco říct o třídě Jet\MVC. A následně už můžete blíže prozkoumat třídu Jet\MVC_Router.