Jet MVC

Jak jistě víte, tak s MVC souvisí pojen routování. V kontextu našeho světa, světa online aplikací, to znamená jak HTTP požadavek převést na nějakou konkrétní akci.

A pochopitelně i v Jet MVC routování je. Ovšem je to trochu komplexnější, ale v reálu velice mocný a užitečný systém, reflektující to co dnes a denně děláme a potřebujeme, který zároveň poskytuje velkou míru svobody a flexibility.

Smyslem není pouze převést nějakou konkrétní URL na nějaký kontroler ( tedy třeba nějakou konkrétní třídu). Ne, Jet to bere víc ze široka a volání nějaké metody nějaké třídy je pouze dílek skládačky, jedna část celého řetězce. A tento princip je nutné pořádně vysvětlit.

Jet má sice svůj router, ale to je jen pomocný nástroj.

Primární jsou pojmy báze (MVC_Base) a stránka (MVC_Page). Pojďme si tyto pojmy vysvětlit.

Co je to báze (Base)

Jak již název napovídá, je to nějaký základ. Vlastně je to bod od kterého se vše odvíjí. Pojďme rovnou do praxe. Jako příklad si vezmeme ukázkovou aplikaci, se kterou je Jet distribuován - tam je ukázka všeho zde uvedeného.

Dejme tomu, že děláte nějaký firemní portál, nebo e-shop, či vlastně cokoliv jiného - nezáleží na tom co konkrétně. Z čeho se taková aplikace bude skládat? Určitě bude mít:

  • Administrační rozhraní
  • Samotný portál, e-shop, .... prostě to co je podstatou a co uvidí široké publikum na internetu (či intranetu).
  • Dost možná bude systém obsahovat nějaký REST API server. Není to pravidlem, ale čím větší projekt, tím je to pravděpodobnější.
  • Velký projekt (hlavně např. e-shop) bude mít také nějaké servisní operace. Importy, exporty, přepočty a propočty ... Všemožné ...

Vše to tvoří jeden projekt. Je to jeden celek, který ve výsledku dělá svou práci.

Ovšem každá ta část projektu je jiná. Administrace bude fungovat jinak než třeba e-shop. Například přihlašování pracovníků firmy do administrace bude úplně separované od přihlašování koncových zákazníků. Stejně tak se budou úplně jinak přihlašovat klienti přes REST API. Spektrum lokalizací API může být úplně jiné, než lokalizace toho co vidí zákazníci. Funkce těchto částí projektu budou úplně jiné, navigace (a její samotný princip) bude úplně jiná ... Jsou to vlastně různé světy v jednom projektu. Různé filozofie, různé přístupu a úplně jiné výstupy, dokonce úplně jiní uživatelé.

A právě s tím Jet počítá - přímo pro takové projekty je navržen.

Tedy první co musí router Jetu udělat je určit o co vlastně jde? Je to URL administrace? Nebo je to URL e-shopu? Nebo něco úplně jiného? Je to naprostý základ.

Ovšem báze není jen pojem sám pro sebe - není to abstraktní věc. Je to existující entita, či řekněme objekt. Blíže na bázi můžete podívat zde. Ale uveďme si nejprve co vše báze zahrnuje:

  • Báze má textový identifikátor (v praxi např. "admin", "rest", "web", "shop"), pomocí kterého můžete získávat její instanci a pracovat s ní.
  • Informace o tom, jaké URL má daná báze (např. URL administrace, URL REST API, kořenovou URL webu atd). Jedna báze může mít N kořenových URL, ale jedna z nich je vždy výchozí.
  • Informace o tom jaké lokalizace (Locales) báze má. Každá báze může mít svou množinu lokalizací. Tedy uživatelské rozhraní administrace můžete mít pouze v jednom jazyce, ale e-shop třeba ve třech lokalizacích.
  • Informace o tom jaké jsou URL jednotlivých lokalizací. Tedy jinou URL bude mít česká verze e-shopu a jinou slovenská mutace.
  • A spoustu dalších (ve skutečnosti velice důležitých) meta informací. Např. zda báze požaduje https, zda je báze z principu neveřejná a "za heslem" (např. administrace), ale i výchozí meta tagy pro HTML hlavičku.
  • Jednotlivé báze a lokalizace lze jednoduše aktivovat / deaktivovat ... 
  • A poslední (ale klíčová) informace: každá báze má svůj inicializátor. Více se dozvíte dále.

Tedy úplně první věc co router udělá je to, že určí k jaké bází HTTP požadavek (tedy daná URL) náleží a dál drží instanci této báze - reálně existující entity.

Dalším krokem je volání inicializátoru báze.

Inicializátor báze

Kde a jak se inicializátor nastavuje se dozvíte později zde, ale pro teď je důležitá informace co je účelem inicializátoru.

Opět z praxe: Například autentizace a autorizace v administraci bude úplně jiná, než přihlášení zákazníka na e-shopu. Bude pracovat s jinou třídou představující uživatele a tak dále (ovšem se stejným kontejnerem a fasádou autorizačního systému - ale nepředbíhejme - o tom si povíme později). Nebo například logování operací bude úplně jiné (resp. opět na venek stejné, ale bude jiný backend). Jednoduše řečeno je nutné zajistit (a nastavit) odlišné základní chování aplikace a celého systému podle toho o jakou část projektu se jedná.

A právě inicializátor donastaví celý systém tak, aby byl připraven na práci s danou bází. Inicializátor je součástí aplikačního prostoru. Je to věc plně pod vaší kontrolou a je jen na vás co vše inicializátor bude dělat.

V tuto chvíli již máme určenou bázi (víme o jakou část projektu půjde), máme zavolaný inicializátor báze a donastavený systém.

A co dál? Dál router začne zjišťovat na jakou stránku (Page) daný požadavek směřuje.

Co je to stránka (Page)

Vezměme si jako příklad to co právě čtete:

https://www.php-jet.net/doc/mvc/plne-vyuziti-mvc-v-php-jet

To je nějaká stránka. Tak je to od počátků věků co web existuje. Ale pojďmé si to ještě rozebrat (a v rámci toho si zrekaputulujeme pojem báze).
URL lze rozdělit na tyto části:

  • https://www.php-jet.net/
    Toto je URL báze. Přesněji báze web a její výchozí lokalizace cs_CZ.
  • doc/
    Toto je URL stránky dokumentace. Tedy entity stránka s patřičnými parametry.
  • mvc/plne-vyuziti-mvc-v-php-jet
    Toto je URL konkrétního článku v rámci dukumentace. Ale k tomu si řekneme něco dále - pro teď si to pojmenujme jako nezpracovaná část URL. Již se nejedná o URL stránky ve smyslu entity stránka v rámci Jet MVC.

Tedy router už v tento moment ví, že se koukáte na web o PHP Jet (určený pro vás), že čtete výchozí lokalizaci cs_CZ a že si prohlížíte stránku s dokumentací.

Instanci báze již dávno máme nachystanou, teď přišlo na řadu vytvoření a držení instance další entity - stránky (Page).

Stránka ještě komplexnější entita než báze. Opět uvedu alespoň stručně pojatý přehled co tato entita zahrnuje:

  • Stránka vždy náleží pod konkrétní bázi.
  • Stránka má textový identifikátor - ID (např. "doc", "download", "contacts" a podobně), který je v rámci dané báze unikátní.
  • Stránka má svou URL. To je velice důležité. Znamená to, že odkazy na stránky v aplikaci nebudete "na tvrdo" zapisovat do HTML kódu, ale používat ty URL, které vygeneruje entita stránka (a když se změní URL stránky, změní se automaticky i odkazy).
  • Stránka má také celou řadu meta informací. Ať už je to interní název stránky, různé titulky (pro HTML, pro menu, pro navigaci, ...), možnost ji učinit stránku neveřejnou a požadovat přihlášení, dokonce i přiřadit ikonu a prioritu. Rovněž nelze nezmínit meta tagy, dokonce je možné definovat HTTP hlavičky a také interní parametry.
  • Stránky jsou pochopitelně uspořádány do stromové struktury.
  • Stránka má obsah!
    Tedy na stránce se musí něco nějak zobrazit. A aby se to "něco" zobrazilo, tak je nutné určit co a jak se má provést ...

U toho faktu že stránka má obsah se zastavme.

Obsah stránky

Způsobů jak může stránka definovat svůj obsah je několik a každý se hodí pro jiný účel. Vezmeme to od toho nejjednoduššího po ten nejkomplexnější (ale nejtypičtější)

Plně statická stránka - statický obsah.

To je ta nejprimitivnější možnost. Součástí definice stránky je HTML kód (nebo klidně prostý text, prostě jakákoliv data a informace) a ten se pošle jako odpověď zpět návštěvníkovi / uživateli. Hotovo, již se neprovádí volání žádných kontrolerů.

Ale k čemu je to dobré? Vezměme reálný příklad z praxe.

Před lety jsme realizovali web jednoho z velkých / hlavních mobilních operátorů. Tento web velice často obsahuje různé samostané mikro weby - tzv. landing pages pro různé marketingové akce. Na ty stránky bylo nutné odkazovat z různých míst webu z obsahu spravovaného CMS. Ovšem stránky samotné navrhovaly a kódovaly (vlastně to tak stále je, pokud správně vím) další agentury, mající na starosti kapaně. Nám prostě dodali statický obsah k nasazení. A taková situace je v Jet velice snadno řešitelná.

A nemusí to být jen tato situace. Pokud máte unikátní plně statickou stránku tak proč nějaké programování? Statickou stránku si můžete například jednoduše naklikat v Jet Studiu. Navíc je to technicky nejrychlejší možnost - z pohledu realizace, ale i z pohledu výkonu webu.

Callback

Toto už je trochu jiná situace. Pokud potřebujete, aby stránku zpracovávala nějaká třída (respektive metoda nějaké třídy), tak můžete definovat která třída a jaká její metoda to bude. Renderování stránky tedy končí tím, že je zavolána tato vaše třída a její statická metoda a dál už je to na vás.

Ovšem i v tomto případě stránka plně figuruje ve struktuře webu, lze na ní odkazovat, dát ji do navigace a tak dále.

Tento způsob je také dobře použitelný pro různé nízkoúrovňové věci - například pro serverové části různých webových služeb a tak podobně.

Sada obsahu

... nebo by se dalo napsat sada obsahů. Tohle je hlavní (a alespoň mnou v praxi nejčastěji používaný) způsob.

To množné číslo ("obsahů") jsem nepoužil náhodou. 

Koukněme se ještě na webové stránky. Třeba právě na tuto, kterou právě čtete - ve své podstatě je tato stránka velice primitivní. Ale i na této primitivní stránce vidíte několik prvků. Úplně nahoře je input pro hledání. Pak nějaké hlavní menu, pak drobečková navigace, pak navigace v rámci dokumentace a v neposlední řadě tento text dokumentace.

Běžná stránka se vlastně vždy skládá z nějakých komponent a každá ta komponenta je na určité pozici v rámci nějakého layoutu

A proto stránka, pokud pracujeme v tomto režimu, nemá jeden obsah, ale N obsahů.

Princip je jednoduchý: Prostě určíte, že tu a tu akci toho a toho modulu chcete poslat na tu a tu pozici v rámci layoutu stránky.

Každý modul se pak stará o to své - o tu část stránky, která mu náleží. Tedy například modul Web.Search (přesněji jeho kontroler Main a akce header_search - o tom všem si povíme později) se stará o zobrazení pole pro hledání, modul Doc.Browser o zobrazení článku a tak dále.

Nezpracovaná část URL

Ještě dlužím vysvětlení tohoto pojmu. Malá rekapitulace: Máme bázi, máme lokalizaci, máme stránku a zůstává nám kus URL, který nenáleží žádné stránce. U této stránky, kterou právě čtete je to mvc/plne-vyuziti-mvc-v-php-jet. A jak vás jistě hned napadlo, tak se jedná o URL tohoto článku z dokumentace.

Ovšem router nemůže vědět co to je - co daný kus URL znamená. Poslední k čemu se router dostal a co rozpoznal je stránka. Pokud tedy router zjistí, že URL ještě pokračuje, tak požádá stránku o vyřešení situace (o resolv).

Stránka provede následující: Pokud má definovanou sadu obsahu, tak postupně požádá všechny kontrolery všech asociovaných modulů o vyřešení situace. Pokud žádný z kontrolerů tento zbytek URL nepozná, tak router zařídí přesměrování na poslední rozpoznanou URL (nebo lze poslat 404, ale přesměrování na poslední rozpoznanou URL je výchozí chování).

V našem konkrétní příkladu (který se týká stránky na kterou právě koukáte) tento konkrétní kousek URL pozná modul Web.Doc.Browser a rovnou zjistí o jaký článek se jedná a vše si připraví k jeho zobrazení článku a zobrazení navigace. Router je informován o tom, že i tato část URL byla zpracována, je to tedy známá URL a vše je v pořádku. Může se tedy provést vše potřebné: zavolat všechny akce všech kontrolerů příslušných modulů a sestavit výslednou stránku.

A co takové REST API?

Celou dobu jsem řešil jak zobrazit například tuto stránku. Ale jak udělat třeba REST API? Ve skutečnosti je to ještě snadnějǐ:

  • Vytvoříte si příslušnou bázi s příslušnou URL (a tu ustanovíte jako neveřejnou - ideálně).
  • Vyřešíte si autentizaci a autorizaci.
  • Můžete vytvářet jednotlivé stránky jako jednotlivé uzly (např. https://api.mujweb/billing/ , http://api.mujweb/orders/) REST API a k nim definovat moduly mající tyto uzly API na starosti.
  • Speciální ovšem bude příslušný kontroler modulu uzpůsobený pro REST API, ale o tom si povíme jinde.
  • To je z hlediska MVC vše ... Např takový layout v kontextu API řešit nemusíte.

Shrňme si to ...

Ve skutečnosti je routování jednoduché:

Zjistit bázi > Zjistit lokalizaci > Zjistit stránku > Odbavit stránku (jednou z několik možností)

Důležité je pochopit smysl entit báze a stránka. Tyto entity jsou základním modelem toho na co koukáte a co web vlastně vždy je - ať je to administrace, e-shop, nebo API, stále jsou to stránky.

Take je důležité vědět, že pro navigaci (tedy tvorbu URL v rámci projektu a její použití kdekoliv - např. v menu a podobně) slouží právě tyto entity (nikoliv na tvrdo zapsané URL někde v HTML kódu).

Když vytváříte novou stránku, tak opravdu vytváříte novou stránku a její definici jako skutečnou neabstraktní entitu, něco co existuje v podobě adresáře a definičního souboru - to se dozvíte dále. Stránku můžete vytvořit ručně, nebo pohodlně naklikat přes Jet Studio.

Nikam nezapisujete žádné "routy", nevytváříte pro stránku novou třídu. Nic takového. Vlastně to má blíž starým dobám, kdy se jednoduše vytvořil soubor doc.php. Jen je to sofistikovanější a mocnější a pro velké projekty nutné. Ale stále je to ve své podstatě jednoduché a pro web a online aplikace přirozené, přímočaré a především neomezující a svobodné.

Předchozí kapitola
MVC - obecně
Další kapitola
Báze / Base