PHP Jet - Architektura - microservices, moduly a MVC

18. 12. 2022 20:12 Mirek Marek

Úvod

Především díky že jste si v tomto možná trochu hektickém předvánočním čase udělali čas na tento článek.

Díky diskuzními příspěvku jednoho z vás (díky kolego a zdravím ;-) ) jsem si uvědomil, jak lze dobře objasnit architekturu a celou myšlenku a filozofii PHP Jet. Mluvím hodně o modularitě, ale to může být dosti matoucí pojem, protože pojem „modul“ se vyskytuje v různých platformách a je chápan různě. Ovšem pojem microservices architecture to ve skutečnosti vystihuje mnohem lépe!

Ale pokud chcete, tak se nejprve mrkněte na zajímavé přednášku dvou kolegů o praktických zkušenostech s microservices: video 1 a video 2. Jeden naznačuje značnou až extrémní složitost, druhý kolega má rovnou skvělou sarkastickou přednášku – styl mi trochu připomíná Monty Python.

A proč tady na blogu nejprve brojím proti tomu, když je něco (zbytečně) složité a teď vytahuji dvě přednášky, které jsou o tom jak něco může být opravdu složité a přinášet komplikace? (viz hlavně 2. video) Protože ta myšlenka a architektura jako taková vůbec není špatná – naopak. Přijde mi to přirozené, ale nesmí se to hnát ad absurdum.

Pravděpodobně víte o co jde, ale pro pořádek stručně objasním, že jde o architekturu systému, kdy aplikaci netvoří monolit, ale různé služby, které mezi sebou komunikují a to pomocí různých API, front zpráv a událostí a tak dále. O úskalích, které to v praxi přináší mluví kolegové v těch přednáškách na základě jejich praktických zkušeností. Je to složité na infrastrukturu, deployment, řízení projektu a tak dále. Ale oni mají microservices architekturu ad absurdum – tedy každá mikro služba má třeba i svůj virtuální server, své API atd. Do tohoto extrému teď rozhodně nepůjdeme, ale inspirujeme tím nejlepším co na té filozofii je.

Ta architektura a myšlenka má řadu výhod. Například můžete zcela jasně někomu delegovat vývoj určité části aplikace / komponenty systému / části projektu určitému člověku nebo týmu. Lze se jasně určit kdo za co zodpovídá, kde má kdo pracovat a jak a především již jednou odvedená práce se dá ideálně mnohokrát použít. Rovněž je to dobré při revizích a testování. Pokud nemáte „šílenou“ infrastrukturu, tak je to vlastně dobré i na deploymennt, protože může být více zřejmé jaké komponenty projektu a proč a kdy je třeba nasadit do provozu (měnili jsme pouze modul X.Y, je jasné že budeme nasazovat ten a tak dále). A celkově je to dobré pro rozvoj aplikace, protože se jednotlivé komponenty mohou vyvíjet nezávisle – bez vlivu jedné na druhou. Pokud tedy nedojde k zásadní změně v celém systému, ale k tomu se také dostanu.

A přesně to vše umožňuje PHP Jet při jeho maximálním a správném použití. Nahlíží na aplikaci (lépe řečeno na projekt) vlastně jako na soubor mikroslužeb, nebo lépe řečeno mikroapliklací. S tím rozdílem, že tyto komponenty spolu komunikují přímo (ovšem na Jetu by bylo snadné implementovat i microservices systém v plném rozsahu se vším všudy – i s API, ale to teď nechme být) a jsou zasazené do společného rámce MVC systému bází a stránek a mezi komponentami systému je úzká vazba (třídy se spolu „baví“ přímo, ne přes API a nějaký aplikační protokol či front zpráv v rámci infrastruktury). 

PHP Jetu naopak nejsou žádné routy, žádné směrování URL na funkce a tak dále. Teda … Trochu kecám … Není to tak úplně pravda, ale pro teď to nekomplikujme – routy v Jet nejsou primární věc, ale věc podružná (týká se mikroaplikací / modulů samotných) – vysvětlím později. Prostě pro teď úplně zapomeňte na to, že logika aplikace je nějak přímo vázána na HTTP požadavek. Pro teď zapomeňte na něco jako ~/routes/web.php a na další podobné věci. To není v PHP Jet primární směr uvažování o aplikaci.

V PHP Jet je modulární vlastně vše. Vše je mikroslužba, nebo mikroaplikace. I když je Jet navržen jako jeden celek, tak je to celek poskládaný z „dílků“, nad kterými máte plnou kontrolu. A můžete využít již předpřipravené „dílky“, nebo si udělat své vlastní. Tedy velice snadno si můžete do firmy vyvinout moduly na to co děláte stále dokola a ty používat na X projektech a pouze je přizpůsobovat. Sami si na PHP Jet můžete udělat své CMS s jeho moduly, nebo e-shop řešení a tak dále a vše stále recyklovat (a postupně vylepšovat, chcete-li).

To se týká i „vnitřností“ Jetu, kde se  k tomu všemu se přidává ještě známý pojem dependency injection, kontejnery a také fasády a továrny. Ale o tom si povíme někdy příště. Ovšem je důležité si uvědomit, že tak jak je „poskládána“ aplikace, tak je poskládán i Jet a není problém si udělat třeba vlastní implementaci stránek a bází a zachovat při tom architekturu i API systému. Ale o tom opravdu víc příště.

Pojďme si to ukázat názorně a to rovnou na zcela nové e-shop platformě vyvíjené na PHP Jet, protože to už je něco reálného a hlavně něco daleko komplexnějšího a lépe se na tom vystihne podstata myšlenky a celé architektury.

Prakticky: Jet Shop – entita zboží a její „mikroslužby“

Každý e-shop má pochopitelně produktový katalog, který tvoří kategorie, parametriace, produkty a nad tím vším asi tak miliarda funkcí a „fičurek“. E-shop je také o objednávkách a jejich zpracování, rovněž o desítkách funkcí určených pro marketing, napojení na další systémy a tak dále. Z toho všeho si teď jako příklad vytáhnu pouze problematiku samotných produktů z produktového katalogu chystané platformy Jet Shop.

Základní entita

Jak říká kolega v jedné z přednášek na které odkazuji v úvodu článku, tak aplikace tvořená mikroslužbami má sice řadu více-méně nezávislých komponent, ale ať děláte co děláte, tak za tím vším je databáze. Ano, může to být databázový cluster z více „mašin“, ale stále je to prostě jedna databáze. Pro pořádek: Ve skutečnosti by to šlo udělat tak, že různé entity budou mít různé databáze. Ale nekomplikujme to.

Zde je tedy něco, co si dovolím nazvat základní entitou. Jedná se o obecnou třídu (třídy) představující danou entitu a její obecný model. Tedy to jaké vlastnosti (informace) entita má. V případě zboží například název, popis, ceny a mnoho dalších parametrů. Neméně důležitou součástí základní entity je obecná bussiness logika nad těmito daty a informacemi (v případě e-shopu a zboží například cenotvorba, či určení dostupnosti zboží a tak dále). Nic méně a nic více. Základní entita neřeší žádné věci vázané na konkrétní HTTP požadavek, žádnou konečnou formu zobrazení informací a tak dále. Řeší prostě obecnou práci s daty a informacemi a obecné algoritmy nad tím.

V případě Jet Shop je zboží reprezentováno hlavní třídou JetShop\Product, které je samozřejmě DataModel a která využívá další subentity jako je JetShop\Product_ShopData (informace vázané na konrkrétní shop a jeho lokalizaci – tedy název, popis, cena, sazba DPH a další), JetShop\Product_Parametr (parametry parametrizace) a tak dále. 

Pochopitelně jsou vhodně využity i traity, protože obecná entita je sama o sobě obsáhlá. Tedy i základní entita je rozdělena na subentity a vlastně by se klidně dalo říct na další mikroslužby. Například logika cenotvorby je samostatný trait, logika dostupnosti další trait, práce s variantami další a tak dále.. Tedy i zde je aplikována logika mikroslužeb – tedy netvořit jeden megacelek, ale snažit se problém dělit na menší, přehledné, jednoznačné a snáze pochopitelné a udržitelné části.

 Entita by se dala popisovat dlouze a obsáhle, ale to pro teď není účelem tohoto článku.

Když vytvořím tuto základní entitu, tak mám obecnou možnost přidávat produkty, mazat, editovat, validovat jejich data, mám implementovanou bussiness logiku cen (vím tedy kolik bude produkt stát v určité sitauci), umím určit jeho dostupnost (je skladem? a kdy skladem bude?)  a tak dále. To vše pochopitelně z pohledu aplikace, ne z pohledu UI. A na základně základní entity (samotné poskládané z mikroslužeb) se již mohou stavět „mikroslužby“ a „mikroaplikace“ – tedy konkrétní operace, konkrétní UI a tak dále.

A pro pořádek: Tyto třídy jsou umístěné v adresáři k tomu určeném. Tedy ~/application/Classes/. To je adresář určený právě pro obecné entity, obecné třídy.

Jaké „miroslužby“ – tedy moduly budu potřebovat?

Díky základní entitě umí aplikace se zbožím obecně pracovat a teď je otázka co se se zbožím bude dít v reálném světě. To už řeší jednotlivé moduly / mikroaplikace / mirkoslužby:

Správa produktů – „mikroaplikace“

Pracovníci e-shopu rozhodně budou potřebovat efektivní a mocný nástroj pro správu produktů. To existuje ve formě modulu Admin.Catalog.Products a už se to nedá nazvat mikroaplikace. Je to projekt sám o sobě a ad absurdum to může mít svého vlastního vývojáře a „vlastníka produktu“. Je to něco, na co uživatelé budou „koukat“ celé dny, protože je to jejich pracovní náplň. Tedy zde už není tak důležité datové modelování a bussiness logika, ale UI/UX a samozřejmě také rychlost aplikaci, rovněž i rychlost implementace toho co uživatelé – pracovníci potřebují.

Exporty

Zboží je pochopitelně nutné exportovat na vše možné od Heureky, přes Google a Seznam až po všemožné další služby. K tomu slouží moduly Exports.*. Každý typ exportu je samostatná mikroaplikace, která data ze základní entity transformuje do požadovaného datového formátu (zpravidla XML). A opět může být někdo, kdo má tuto oblast na starosti, plně se stará o své má jasně dané pole působnosti.

Exporty vůbec nemusí být aplikace reagující ha HTTP požadavek, ale CLI aplikace.

Napojení na tržiště

Pochopitelně nesmí chybět napojení na různá tržiště. Toto napojení se stará o aktualizaci nabídky produktů, skladové dostupnosti a tak dále.

Zpravidla jde o několik modulů. Některé jsou nástroje do administrace, některé CLI mikroaplikace zajišťující samotnou synchronizaci dat a tak dále.

Napojení na účetnictví

A pochopitelně je třeba synchronizovat ceníky, skladové blokace a zásoby a tak dále … Opět další modul, či moduly a CLI mikroaplikace a opět samostatné téma.

Procházení katalogu

Pochopitelně musí existovat „mikroaplikace“ určená pro zákazníky, pomocí které budou procházet nabídku e-shopu, filtrovat, zobrazovat a tak dále.

… A mnohé další …

To pochopitelně není vše, ale účelem článku není popsat část platformy Jet Shop, ale vysvětlit, proč nahlížím na aplikace tak jak nahlížím a proč je PHP Jet takový jaký je.

Jet Shop – shrnutí

Jak je z popisu patrné, tak takový projekt nesmí být monolit už z principu. Respektive nedokážu si představit, že bych takový projekt realizoval jinak než s pomocí modulárního uvažování – architektury a technologie, které je tato filozofie vlastní a klade na ní důraz.

A zároveň je patrné jaké to má výhody. Představme si teď, že na projektu pracuje opravdu velká firma, která má možnost na každou z komponent delegovat vlastní mikrotým lidí. Každý ten mikrotým se může věnovat své práci a má jasně dané kde se v projektu pohybuje. V rámci PHP Jet je to dané tím, že je aplikace ideálně rozdělená na moduly mající své adresáře. Jeden mikrotým se nebude plést do práce mikrotýmu druhému. Když se mění jedna komponenta, neovlivňuje to komponentu druhou. Na jakých to poběží URL a na jaké HTTP požadavky to bude reagovat? To v tomto kontextu vůbec není důležité. I to je zapouzdřené v modulech a MVC. Prostě práce je dobře izolována a jasně rozdělena na všech úrovních.

Ale co když se má změnit něco obecného? Třeba přijde nová cenotvorba? Konkrétně: Co se bude dít až nějaká země bude přecházet z národní měny na EURa? Ponechme si pro názornost představu, že každá komponenta má svůj mikrotým vývojářů. Změna tak podstatné věci jako je například zmíněná změna cenotvorby v souvislosti se změnou měny vlastně znamená změnu všeho – nebo minimálně analýzu dopadů a testování. Ale ta změna musí opět začít u základní entity. A je to něco, co donutí v takové situaci mikrotými vývojářů spolu komunikovat. Projekt se v takovém případě za účelem vyřešení problému změní přirozeně (a dočasně) z izolovaných mikroprojektů v projekt spojený, kde spolu prostě všichni musí mluvit a musí si vše ujasnit. A správná komunikace je základ úspěchu! Další výhodou je, že k implementaci takových změn prostě musí dojít nejprve na jednom místě – v třídách základní entity. Tím je přirozeně zařízeno, že v týmu dohodnutá změna bude implementována jednotně a jednoznačně, s přihlédnutím k potřebám všech „mikroslužeb“ / „mikroaplikací“ a bude také všemi zkontrolována a prověřena. Suma sumárum i při tak náročné změně je přirozeně jasné kde změna začíná, proč a jak se to děje a jaký má být postup implementace změn. Architektura systému tedy determinuje postup vývoje a ten je pak přirozený, efektivní a více hladký (pochopitelně pokud to všichni správně pochopí a šéf to dobře uřídí).

Prakticky ještě jednou: ukázková aplikace

Pryč s teoretizováním o Jet Shopu, na který si (zatím) nemůžete sáhnout. V ukázková aplikaci se kterou se PHP Jet distribuován najdete totéž, pouze v daleko menším měřítku. Mimo jiné ukázková aplikace obsahuje malý systém pro psaní článků a na ten si můžete „sáhnout“ okamžitě.

To zahrnuje následující:

  • Základní entitu článek
    To jest třídy
    JetApplication\Content_Article
    a
    JetApplication\Content_Ar­ticle_Localized
    (slouží rovnou jako ukázka toho, jak se dá vyřešit lokalizovatelnost obsahu).

    Třídy naleznete samozřejmě v adresáři ~/application/Classes
  • Modul pro správu článků v administraci
    Jedná se o modul mikroaplikaci Content.Articles.Admin, který naleznete v adresáři ~/application/Modules/Con­tent/Articles/Admin.
  • Modul pro prohlížení článků na webu
    Články je nutné nějak prezentovat návštěvníkům a o to se stará modul / mikroaplikace Content.Articles.Browser, který najdete v adresáři ~/application/Modules/Con­tent/Articles/Browser
  • Modul REST API pro práci s články přes API
    A pochopitelně nesmí chybět ukázka toho, jak nad základní entitou vytvořit REST API. O to se stará modul Content.Articles.REST, v adresáři ~/application/Modules/Con­tent/Articles/REST.

    Ale tento modul je opravdu velice jednoduchý.

Všimněte si prosím, že vůbec neřeším na jakých URL jsou jednotlivé komponenty. Ne že by to nebylo důležité, ale pro teď to nehrálo vůbec žádnou roli. Struktura aplikace z hlediska URL adres a navigace je také důležitá, ale je to samostatné téma. To jestli jsou články vypsány na URL X, nebo URL Y, nebo kde je REST API, kde je administrace …, to nemá co do činění se základní logikou aplikace. Tedy nesouvisí to s tím jak se operuje s daty a informacemi a logikou a algoritmy nad tím. Ale jak jsem již řekl, tak struktura a navigace online aplikace je věc důležitá, ale je to samostatné téma, které s modularitou systému spolupracuje – ovšem nic nediktuje. Pojďme se na to kouknout.

Jet MVC a modularita

Pro jednoduchost teď zůstaňme u práce s články v ukázkové aplikaci. 

Kontrolní otázka: Hraje pro aplikační logiku nějakou roli, zda mám zobrazování článků na URL https://jet.lc/clanky/, nebo https://jet.lc/en/articles/, nebo se rozhodnu vytvořit si blog na adrese https://jet.lc/blog/? 

Nehraje, proč by to také mělo hrát nějakou roli? Proč by ta URL vůbec měla být svázána s aplikační logikou? Vždyť by mě to pouze omezovalo … Modul / mikroaplikace Content.Articles.Browser funguje stále stejně ať je na jakékoliv URL. Stejně jako TODO list, který jsem vytvářel jako ukázku v minulém článku. Ano, modul se může přizpůsobit tomu na jaké stránce zrovna je umístěn a podle řady kritérií (např podle parametrů stránky – viz minulý článek), ale to už je čistě věc toho jak je modul implementován. Ale to jestli je základní URL kde modul je taková či onaká z pohledu modulu vůbec nehraje roli.

Myšlenka je následující: Jet MVC má dvě základní entity: bázi a stránku a pro úplnost dodejme, že v tom hraje i roli lokalizace, ale o tom si povíme víc příště.

Báze je základní entita. Ta určuje na jaké základní URL je například administrace, veřejná část, servisní skripty a další hlavní oblasti online aplikace. Bází může být libovolné množství, podle toho na kolik základních celků je projekt rozdělen. Báze také určuje to, jak se bude v dané části aplikace / projektu například bude chovat autentizace a autorizace (a další systémové komponenty) a to díky tomu, že při inicializaci vloží závislosti do kontejnerů v podobě fasád (budu psát příště, jak jsem již slíbil). Tedy přihlašování do administrace bude operovat s úplně jinou skupinou uživatelů a může se fakticky chovat zcela odlišně než se chová autentizace a autorizace REST API. Ovšem aplikace komunikuje s autentizací a autorizací stále přes stejné API. Ale to už odbočuji jinam :-)  Prostě: Báze určuje, že na URL XYZ se nalézá ta a ta část projektu (web, administrace, API a tak dále) a že se ta část projektu v obecných věcech chová určitým způsobem.

Stránka je již „něco“ na nějaké URL. Tedy opravdu prostě stránka. Každá stránka spadá pod určitou bázi, opět nese řadu metainformací (například jaký má mít základní titulek, popis v navigaci, ale třeba i ikonu, pokud je třeba a tak dále) a stránka má pochopitelně především obsah. Krom toho, že obsah stránky může být v základní situaci jednoduše statický, tak stránka na sebe primárně váže moduly, tedy mirkoaplikace

Pochopitelně v tom hraje podstatnou roli i fakt, že stránka má nějaké rozvržení / layout což není nic jiného než view skript a layoutů si můžete vytvořit kolik chcete a potřebujete. Najdete je například v adresáři ~/application/bases/web/layouts. Každá báze má svou sadu layoutů. Tedy báze admin má své layouty v adresáři ~/application/bases/admin/layouts a tak dále. Pochopitelně si můžete vytvářet báze jak chcete a kolik chcete a to samé platí o layoutech. Specifikem layoutů oproti běžným view skriptům je fakt, že mohou (respektive mají) definovat pozice na které se následně umisťuje další obsah a také to, že jsou schopny rovnou vložit určité obecné moduly. To znamená, že třeba o drobečkovou navigaci a její zobrazení se také stará modul, který však rovnou určí layout. A takových modulů může layout vložit kolik potřebuje, stejně jako je možné definovat tolik pozic pro obsah kolik je třeba.

Zpět ke stránce. Stránka na sebe tedy váže layout a také moduly. Stránka určuje, že na ní „poběží“ ten a ten modul – mikroaplikace a že výstup bude umístěn na určitou pozici v layoutu. 

Tedy v Jet Studiu si snadno naklikáte stránku. Nebo její definici vytvoříte ručně – Jet Studio není povinné používat! A na stránku „pověsíte“ moduly, které umístíte na jednu z pozic, které nabízí layout stránky. Nic víc na tom není. Je to takto primitivní. 

Systém Jet MVC a báze a stránky řeší pořádně a důkladně to co řešit má. Tedy strukturu aplikace, navigaci v rámci aplikaci (včetně tvorby odkazů), nese celou řadu meta informací a to vše je možné díky továrnám rozšířit či reimplementovat se zachováním základního API a tedy funkčnosti celého systému (a už zase odbočuji ;-) – o tom příště …)

Ovšem nějaké routy? Pojmenované routy? Routy v odkazech? Mega-definice rout (pro velký projekt) v nějakém souboru? Ne … Pracuje se s metainformacemi bází a stránek (i při vytváření URL) a nemá to přímý vliv na aplikační logiku. Protože je to prostě stále web, ale web poskládaný z mikroaplikací. 

Ale ano, routy v Jet jsou ( k tomu se záhy konečně dostanu ), ale je to vnitřní věc modulů – mikroaplikací. V žádném případě to není globální záležitost, žádný adresář routes prostě nehledejte. Místo něj jsou tu adresáře „application/bases“ a v podadresáře bází adresáře „pages“, ale to vše primárně determinuje navigační uspořádání celého projektu a pochopitelně také to, kde jaký modul „poběží“.

Routy v PHP Jet

Tak a teď jak je to s těmi routami v PHP Jet co nejsou a vlastně jsou:

  • Neexistuje nic jako globální routy.
  • Každý modul (respektive kontroler(y) modulu – mikroapliakci) si řeší své vnitřní routování sám.
  • V rámci modulů / jejich kontrolerů je routování rozhodovací mechanismus o tom, jaká akce kontroleru se provede, ale to na základě mnoha aspektů – pomocí algoritmu, který definuje vývojář a nemusí vůbec jít o přímou vazbu na HTTP požadavek, ale mohou být vyhodnoceny libovolné jiné okolnosti.

Tedy: Ano, bez routování by to nešlo a hned si ukážeme jak je v PHP Jet toto řešeno. Ale určitě není žádný důvod aby se routovací mechanismus výpisu článků (například)  míchal s routovacím mechanismem čehokoliv jiného - a to jakkoliv, ani pomocí group a podobných záležitostí – pro mé projekty takový přístup prostě není ideální.

Pojďme mrknout jak to PHP Jet řeší konkrétně.

Metoda resolve() : bool|string

Základní princip je jednoduchý. Každý kontroler modulu dědí od třídy Jet\MVC_Controller (případně od některého ze specializovaných potomků jako je například Jet\MVC_Controller_REST). Každý kontroler má metody resolve() : bool|string a právě ta modulu slouží k jeho vlastnímu routování.

Funguje to tak, že Jet MVC určí o jakou se jedná bázi, o jakou se jedná stránku a jaké jsou na stránce moduly a tedy i kontrolery modulů. (Celé je to velice optimalizovaný a rychlý proces.) Před tím než Jet jednotlivé kontrolery modulů „zavolá“ (tedy zavolá jejich konkrétní metody představující požadovanou akci), tak nejprve zavolá právě onu metodu resolve(). Pokud tuto metodu implementujete, tak v ní máte možnost implementovat libovolný rozhodující algoritmus. Můžete rozhodnout na základě čehokoliv co se stane – jaká metoda kontroleru (tedy akce) bude zavolána.

Metoda resolve() má návratovou hodnotu bool, nebo string. To znamená následující:

  • true: Bude provedena ta akce, kterou definuje stránka (tedy žádná změna, vše „jede dál“).
  • false: Znamená, že požadavek není z nějakého důvodu pro daný modul a kontroler relevantní – modul / kontroler vůbec nebude volán (a tedy ani zobrazen).
  • string: Rozhodovací algoritmus určil, že bude zavolána konkrétní akce. To prakticky znamená, že když metoda vrátí hodnotu „detail“, bude zavolána metoda detail_Action() : void, pokud metoda resolve() vrátí hodnotu „listing“, bude volána metoda listing_Action() : void a tak dále. (Poznámka: Metody představující akce jsou sufixem _Action záměrně odlišeny od ostatních metod kontroleru.)

Metoda getControllerRouter(): MVC_Controller_Router

Stále platí, že základem je metoda resolv(), kde je možné implementovat libovolný rozhodovací algoritmus. Ale v praxi je implementace takových mechanismů neustále dokola stále to samé – tedy pořádná otrava. Proto přichází na řadu mikrorouter kontroleru. Ale pěkně popořadě.

Výchozí implementace metody resolve() má tuto podobu:

public function resolve(): bool|string
{
    $router = $this->getControllerRouter();
    if( !$router ) {
        return true;
    }
    return $router->resolve();
}

Tedy kontroler ve výchozím stavu předpokládá, že metoda getControllerRouter() může vrátit instanci routeru a ten pak provede vyhodnocení.

Router je instance třídy Jet\MVC_Controller_Router což je tak zvaný mikrorouter kontroleru.

Ve své podstatě je tento mikrorouter opravdu jednoduché. Nedefinují se v něm a priory typy a URL HTTP požadavků – ne, na to opět v kontextu Jet prosím zapomeňte. Mikrorouteru kontroleru definujete primárně akci kontroleru a na ní navážete rozhodovací mechanismus

Ukažme si to konrkétně na ukázkové aplikaci:

 Modul Content.Articles.Browser, a jeho hlavní kontroler (třída JetApplicationModu­le\Content\Articles\Browser\Con­troller_Main) má akci „detail“ pro zobrazení detailu článku, tedy metodu detail_Action(): void.

Každý článek má svou URL odvozenou od titulku.

Tedy například: https://jet.lc/clan­ky/konecne-budou-bile-vanoce.html

Část URL „https://jet.lc/“ určuje bázi (tedy zde bázi web), část URL „clanky/“ určuje stránku na které je modul Content.Articles.Brwoser. A kontroler tohoto modulu / mikroaplikace musí ověřit, zda je to opravdu URL nějakého „rozkliknutého“ článku. Pochopitelně URL báze a stránky modul již nezajímá – o to se postaral Jet. Kontorler a jeho router se tedy bude zajímat o zbytek URL, to jest o „konecne-budou-bile-vanoce.html“ a pokud je to opravdu URL článku, tak jej zobrazí. Pokud ne, je to neplatná URL a s tím už opět naloží Jet.

Cílem tedy je URL článku (pokud je platná) namapovat na metodu:

JetApplicationModule\Conten­t\Articles\Browser\Contro­ller_Main->detail_Action()

Ovšem namapování akce proběhne již s použitím mikrouteru kontoleru. Proto v uvedené třídě najdete metodu getControllerRouter(), která má (ve zkrácené verzi) toto podobu:

public function getControllerRouter(): MVC_Controller_Router
{
    if( !$this->router ) {
        $this->router = new MVC_Controller_Router( $this );

        $path = MVC::getRouter()->getUrlPath();

        $this->router->addAction( 'list' )
            ->setResolver( function() use ( $path ) {
                .....
            } );
        $this->router->addAction( 'detail' )
            ->setResolver( function() use ( $path ) {
                if( $path == '' ) {
                    return false;
                }

                $current_article = Content_Article::resolveArticleByURL( $path, MVC::getLocale() );

                if( !$current_article ) {
                    return false;
                }
                $this->article = $current_article;
                MVC::getRouter()->setUsedUrlPath( $path );

                MVC::getPage()->setCacheContext($current_article->getId());

                return true;

            } );
    }

    return $this->router;
}

Teď si to trochu rozeberme:

  • Pro router je primární entitou akce kontroleru na kterou bude rozhodování směřovat. Tedy pomocí metody addAction(‚detail‘) jsou přidávány akce kontroleru, nikoliv URL, druhy HTTP požadavku a podobně. Nic takového. Mikrorouter kontroleru rozhoduje o akcích modulu na základě libovolných kritérií (viz dále).
  • Rozhodování o tom, zda má provést právě daná akce se provádí pomocí anonymní funkce předané metodou setResolver( callable ) : static.
  • Rozhodovací proces zahrnuje to co je třeba a není ničím limitován, na nic pevně vázán.

    Tedy v tomto případě je pro rozhodnutí relevantní část cesty z URL, kterou zachytil hlavní MVC router a tato část URL již nepatří žádné stránce:

    $path = MVC::getRouter()->getUrlPath();

    Dále rozhodující proces určí zda tato část URL vůbec existuje a pokud ano, zeptá se základní entity zda v aktuální lokalizaci existuje článek, který takovou URL má:

    $current_article = Content_Article::resolveArticleByURL( $path, MVC::getLocale() );

    Pokud článek existuje, pak resolver řekne hlavnímu routeru, že daná URL je platná a použita:

    MVC::getRouter()->setUsedUrlPath( $path );
    (Jet musí vědět, že URL je platná, jinak bude URL považovat za neplatnou a provede přesměrování, případně 404)

    Instance článku je předána do vlastnosti kontroleru, aby článek již nemusel být opětovně nahráván:

    $this->article = $current_article;

    A také je informována stránka o kontextu keše (ale to je na jiné povídání).

    MVC::getPage()->setCacheContext($current_article->getId());

    Rozhodovací proces vrací true, to znamená že daná namapovaná akce kontroleru (zde „detail“, tedy metoda detail_Action(): void ) je platná a rozhodovací proces může skočit, akce byla nalezena a bude předána MVC k vykonání. Kontorler zároveň již ví jaký článek má být zobrazen a nebude tedy nutné jej znovu nahrávat z databáze.

mikrorouteru kontroleru by se toho dalo napsat ještě víc, ale to bych zde suploval dokumentaci.

Routy – shrnutí

Ano, v PHP Jet jsou routy, ale nahlíží se na ně jinak:

  • Routy nejsou globální.
  • Routy nesuplují navigační strukturu aplikace. Od struktury a navigace je tu systém bází a stránek.
  • Na strukturu bází a stránek jsou vázané moduly – tedy mikroaplikace / mikroslužby.
  • Systém bází a stránek poskytuje navigační rámec a vše s tím spojené pro mikroaplikace / mikroslužby – tedy moduly.
  • Routery v kontrolerech modulů nahlíží na definici opačně než bývá běžné. Není definována žádná routa popisující parametry HTTP požadavku (typ, URL, regulární výraz a podobně), ale primárně název akce kontroleru na kterou má „routa“ směřovat a k ní je vázaný rozhodující mechanismus, který určí zda je „routa“ platná na základě libovolných kritérií – ne nutně pouze na základě stavu HTTP požadavku. 
  • Rozhodující mechanismus rovnou může provést potřebné přípavné operace tak, aby se akce kontroleru mohla zaměřit na to co je jejím primárním účelem a o rozhodovací proces se již nemusela vůbec starat, případně aby došlo k úspoře systémových prostředků – k optimalizaci.

Závěrem …

Doufám, že se mi dnes podařilo trochu více přiblížit jak je PHP Jet navržen a proč tomu tak je.

Teď pár obecných věcí.

Tentokrát jsem nevytvářel žádné video – v tomto případě jsem k tomu neviděl důvod. Ale děkuji moc (nevím zda můžu jmenovat, ale kolega ví) za rady se zvukem a až příště budu video opět vytvářet, tak věřím, že to bude lepší.

Momentálně pro vás chystám další věc – nástroj pro jednoduchý deployment, který již mnoho let používáme, ale připravuji jeho zbrusu novou verzi určenou k uvedení na světlo světa. Samozřejmě je nástroj postavený plně na Jet. Deployer v dohledné době uveřejním a dám k dispozici a napíši o tom článek.

Dále zvažuji cesty, jak zřídit fórum. Každopádně pro zatím platí, že kdo má dotazy, může kontaktovat přímo mou maličkost e-mailem (viz stránky php-jet.net)

Zatím vám všem děkuji za pozornost, některým i za pomoc a přeji vám všem klidný předvánoční a vánoční čas! Brzy se opět ozvu.

Sdílet