Jak fungují aplikace?
Právě čtete základní listinu dokumentace Nette. Dozvíte se celý princip fungování webových aplikací. Pěkně od A do Z, od chvíle zrození až do posledního vydechnutí PHP skriptu. Po přečtení budete vědět:
- jak to celé funguje
- co je to Bootstrap, Presenter a DI kontejner
- jak vypadá adresářová struktura
Adresářová struktura
Otevřete si příklad skeletonu webové aplikace zvané WebProject a při čtení se můžete dívat na soubory, o kterých je řeč.
Adresářová struktura vypadá nějak takto:
web-project/ ├── app/ ← adresář s aplikací │ ├── Core/ ← základní třídy nutné pro chod │ │ └── RouterFactory.php ← konfigurace URL adres │ ├── UI/ ← presentery, šablony & spol. │ │ ├── @layout.latte ← šablona layoutu │ │ └── Home/ ← adresář presenteru Home │ │ ├── HomePresenter.php ← třída presenteru Home │ │ └── default.latte ← šablona akce default │ └── Bootstrap.php ← zaváděcí třída Bootstrap ├── bin/ ← skripty spouštěné z příkazové řádky ├── config/ ← konfigurační soubory │ ├── common.neon │ └── services.neon ├── log/ ← logované chyby ├── temp/ ← dočasné soubory, cache, … ├── vendor/ ← knihovny instalované Composerem │ ├── ... │ └── autoload.php ← autoloading všech nainstalovaných balíčků ├── www/ ← veřejný adresář neboli document-root projektu │ ├── .htaccess ← pravidla mod_rewrite │ └── index.php ← prvotní soubor, kterým se aplikace spouští └── .htaccess ← zakazuje přístup do všech adresářů krom www
Adresářovou strukturu můžete jakkoliv měnit, složky přejmenovat či přesunout, a poté pouze upravit cesty k
log/
a temp/
v souboru Bootstrap.php
a dále cestu k tomuto souboru v
composer.json
v sekci autoload
. Nic víc, žádná složitá rekonfigurace, žádné změny konstant.
Nette totiž disponuje chytrou autodetekcí.
U trošku větších aplikací můžeme složky s presentery a šablonami rozčlenit na disku do podadresářů a třídy do jmenných prostorů, kterým říkáme moduly.
Adresář www/
představuje tzv. veřejný adresář neboli document-root projektu. Můžete jej přejmenovat bez
nutnosti cokoliv dalšího nastavovat na straně aplikace. Jen je potřeba nakonfigurovat hosting tak, aby
document-root mířil do tohoto adresáře.
WebProject si můžete také rovnou stáhnout včetně Nette a to pomocí Composeru:
composer create-project nette/web-project
Na Linuxu nebo macOS nastavte adresářům log/
a temp/
práva pro zápis.
Aplikace WebProject je připravená ke spuštění, není třeba vůbec nic konfigurovat a můžete ji rovnou zobrazit
v prohlížeči přístupem ke složce www/
.
HTTP požadavek
Vše začíná ve chvíli, kdy uživatel v prohlížeči otevře stránku. Tedy když prohlížeč zaklepe na server s HTTP
požadavkem. Požadavek míří na jediný PHP soubor, který se nachází ve veřejném adresáři www/
, a tím je
index.php
. Dejme tomu, že jde o požadavek na adresu https://example.com/product/123
. Díky vhodnému
nastavení serveru se i tohle URL
mapuje na soubor index.php
a ten se vykoná.
Jeho úkolem je:
- inicializovat prostředí
- získat továrnu
- spustit Nette aplikaci, která vyřídí požadavek
Jakou že továrnu? Nevyrábíme přece traktory, ale webové stránky! Vydržte, hned se to vysvětlí.
Slovy „inicializace prostředí“ myslíme například to, že se aktivuje Tracy, což je úžasný nástroj pro logování nebo vizualizaci chyb. Na produkčním serveru chyby loguje, na vývojovém rovnou zobrazuje. Tudíž k inicializaci patří i rozhodnutí, zda web běží v produkčním nebo vývojářském režimu. K tomu Nette používá autodetekci: pokud web spouštíte na localhost, běží v režimu vývojářském. Nemusíte tak nic konfigurovat a aplikace je rovnou připravena jak pro vývoj, tak ostré nasazení. Tyhle kroky se provádějí a jsou podrobně rozepsané v kapitole o třídě Bootstrap.
Třetím bodem (ano, druhý jsme přeskočili, ale vrátíme se k němu) je spuštění aplikace. Vyřizování HTTP
požadavků má v Nette na starosti třída Nette\Application\Application
(dále Application
), takže
když říkáme spustit aplikaci, myslíme tím konkrétně zavolání metody s příznačným názvem run()
na
objektu této třídy.
Nette je mentor, který vás vede k psaní čistých aplikací podle osvědčených metodik. A jedna z těch naprosto
nejosvědčenějších se nazývá dependency injection, zkráceně DI. V tuto chvíli vás nechceme zatěžovat
vysvětlováním DI, od toho je tu samostatná kapitola, podstatný je
důsledek, že klíčové objekty nám bude obvykle vytvářet továrna na objekty, které se říká DI kontejner
(zkráceně DIC). Ano, to je ta továrna, o které byla před chvíli řeč. A vyrobí nám i objekt Application
,
proto potřebujeme nejprve kontejner. Získáme jej pomocí třídy Configurator
a necháme jej vyrobit objekt
Application
, zavoláme na něm metodu run()
a tím se spustí Nette aplikace. Přesně tohle se děje
v souboru index.php.
Nette Application
Třída Application má jediný úkol: odpovědět na HTTP požadavek.
Aplikace psané v Nette se člení do spousty tzv. presenterů (v jiných frameworcích se můžete setkat s termínem controller, jde o totéž), což jsou třídy, z nichž každá představuje nějakou konkrétní stránku webu: např. homepage; produkt v e-shopu; přihlašovací formulář; sitemap feed atd. Aplikace může mít od jednoho po tisíce presenterů.
Application začne tím, že požádá tzv. router, aby rozhodl, kterému z presenterů předat aktuální požadavek
k vyřízení. Router rozhodne, čí je to zodpovědnost. Podívá se na vstupní URL
https://example.com/product/123
a na základě toho, jak je nastavený, rozhodne, že tohle je práce např. pro
presenter Product
, po kterém bude chtít jako akci zobrazení (show
) produktu s
id: 123
. Dvojici presenter + akce je dobrým zvykem zapisovat oddělené dvojtečkou jako
Product:show
.
Tedy router transformoval URL na dvojici Presenter:action
+ parametry, v našem případě
Product:show
+ id: 123
. Jak takový router vypadá se můžete podívat v souboru
app/Core/RouterFactory.php
a podrobně ho popisujeme v kapitole Routing.
Pojďme dál. Application už zná jméno presenteru a může pokračovat dál. Tím že vyrobí objekt třídy
ProductPresenter
, což je kód presenteru Product
. Přesněji řečeno, požádá DI kontejner, aby
presenter vyrobil, protože od vyrábění je tu on.
Presenter může vypadat třeba takto:
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ProductRepository $repository,
) {
}
public function renderShow(int $id): void
{
// získáme data z modelu a předáme do šablony
$this->template->product = $this->repository->getProduct($id);
}
}
Vyřizování požadavku přebírá presenter. A úkol zní jasně: proveď akci show
s id: 123
.
Což v řeči presenterů znamená, že se zavolá metoda renderShow()
a v parametru $id
dostane
123
.
Presenter může obsluhovat více akcí, tedy mít více metod render<Akce>()
. Ale doporučujeme navrhovat
presentery s jednou nebo co nejméně akcemi.
Takže, zavolala se metoda renderShow(123)
, jejíž kód je sice smyšlený příklad, ale můžete na něm
vidět, jak se předávají data do šablony, tedy zápisem do $this->template
.
Následně presenter vrátí odpověď. Tou může být HTML stránka, obrázek, XML dokument, odeslání souboru z disku,
JSON nebo třeba přesměrování na jinou stránku. Důležité je, že pokud explicitně neřekneme, jak má odpovědět (což
je případ ProductPresenter
), bude odpovědí vykreslení šablony s HTML stránkou. Proč? Protože v 99 %
případů chceme vykreslit šablonu, tudíž presenter tohle chování bere jako výchozí a chce nám ulehčit práci. To je
smyslem Nette.
Nemusíme ani uvádět, jakou šablonu vykreslit, cestu k ní si odvodí sám. V případě akce show
jednodušše zkusí načíst šablonu show.latte
v adresáři s třídou ProductPresenter
. Taktéž se
pokusí dohledat layout v souboru @layout.latte
(podrobněji o dohledávání šablon).
A následně šablony vykreslí. Tím je úkol presenteru i celé aplikace dokonán a dílo jest završeno. Pokud by šablona neexistovala, vrátí se stránka s chybou 404. Více se o presenterech dočtete na stránce Presentery.
Pro jistotu, zkusme si zrekapitulovat celý proces s trošku jinou URL:
- URL bude
https://example.com
- bootujeme aplikaci, vytvoří se kontejner a spustí
Application::run()
- router URL dekóduje jako dvojici
Home:default
- vytvoří se objekt třídy
HomePresenter
- zavolá se metoda
renderDefault()
(pokud existuje) - vykreslí se šablona např.
default.latte
s layoutem např.@layout.latte
Možná jste se teď setkali s velkou spoustou nových pojmů, ale věříme, že dávají smysl. Tvorba aplikací v Nette je ohromná pohodička.
Šablony
Když už přišla řeč na šablony, v Nette se používá šablonovací systém Latte. Proto taky ty koncovky .latte
u šablon. Latte se používá jednak
proto, že jde o nejlépe zabezpečený šablonovací systém pro PHP, a zároveň také systém nejintuitivnější. Nemusíte
se učit mnoho nového, vystačíte si se znalostí PHP a několika značek. Všechno se dozvíte v dokumentaci.
V šabloně se vytvářejí odkazy na další presentery & akce takto:
<a n:href="Product:show $productId">detail produktu</a>
Prostě místo reálného URL napíšete známý pár Presenter:action
a uvedete případné parametry. Trik je
v tom n:href
, které říká, že tento atribut zpracuje Nette. A vygeneruje:
<a href="/product/456">detail produktu</a>
Generování URL má na starosti už dříve zmíněný router. Totiž routery v Nette jsou výjimečné tím, že dokáží provádět nejen transformace z URL na dvojici presenter:action, ale také obráceně, tedy z názvu presenteru + akce + parametrů vygenerovat URL. Díky tomu v Nette můžete úplně změnit tvary URL v celé hotové aplikaci, aniž byste změnili jediný znak v šabloně nebo presenteru. Jen tím, že upravíte router. Také díky tomu funguje tzv. kanonizace, což je další unikátní vlastnost Nette, která přispívá k lepšímu SEO (optimalizaci nalezitelnosti na internetu) tím, že automaticky zabraňuje existenci duplicitního obsahu na různých URL. Hodně programátorů to považuje za ohromující.
Interaktivní komponenty
O presenterech vám musíme prozradit ještě jednu věc: mají v sobě zabudovaný komponentový systém. Něco podobného mohou pamětníci znát z Delphi nebo ASP.NET Web Forms, na něčem vzdáleně podobném je postaven React nebo Vue.js. Ve světě PHP frameworků jde o naprosto unikátní záležitost.
Komponenty jsou samostatné znovupoužitelné celky, které vkládáme do stránek (tedy presenterů). Mohou to být formuláře, datagridy, menu, hlasovací ankety, vlastně cokoliv, co má smysl používat opakovaně. Můžeme vytvářet vlastní komponenty nebo používat některé z ohromné nabídky open source komponent.
Komponenty zásadním způsobem ovlivňují přístup k tvorbě aplikacím. Otevřou vám nové možnosti skládání stránek z předpřipravených jednotek. A navíc mají něco společného s Hollywoodem.
DI kontejner a konfigurace
DI kontejner neboli továrna na objekty je srdce celé aplikace.
Nemějte obavy, není to žádný magický black box, jak by se třeba mohlo z předchozích řádků zdát. Vlastně je to
jedna docela nudná PHP třída, kterou vygeneruje Nette a uloží do adresáře s cache. Má spoustu metod pojmenovaných jako
createServiceAbcd()
a každá z nich umí vyrobit a vrátit nějaký objekt. Ano, je tam i metoda
createServiceApplication()
, která vyrobí Nette\Application\Application
, který jsme potřebovali
v souboru index.php
pro spuštění aplikace. A jsou tam metody vyrábějící jednotlivé presentery.
A tak dále.
Objektům, které DI kontejner vytváří, se z nějakého důvodu říká služby.
Co je na této třídě opravdu speciálního, tak že ji neprogramujete vy, ale framework. On skutečně vygeneruje PHP kód a
uloží ho na disk. Vy jen dáváte instrukce, jaké objekty má umět kontejner vyrábět a jak přesně. A tyhle instrukce jsou
zapsané v konfiguračních souborech, pro které se používá formát
NEON a tedy mají i příponu .neon
.
Konfigurační soubory slouží čistě k instruování DI kontejneru. Takže když například uvedu v sekci session volbu expiration: 14 days
, tak DI kontejner při vytváření objektu
Nette\Http\Session
reprezentujícího session zavolá jeho metodu setExpiration('14 days')
a tím se
konfigurace stane realitou.
Je tu pro vás připravená celá kapitola popisující, co vše lze konfigurovat a jak definovat vlastní služby.
Jakmile do vytváření služeb trošku proniknete, narazíte na slovo autowiring. To je vychytávka, která vám neuvěřitelným způsobem zjednoduší život. Umí automaticky předávat objekty tam, kde je potřebujete (třeba v konstruktorech vašich tříd), aniž byste museli cokoliv dělat. Zjistíte, že DI kontejner v Nette je malý zázrak.
Kam dál?
Prošli jsme si základní principy aplikací v Nette. Zatím velmi povrchně, ale brzy proniknete do hloubky a časem vytvoříte báječné webové aplikace. Kam pokračovat dál? Vyzkoušeli jste si už tutoriál Píšeme první aplikaci?
Kromě výše popsaného disponuje Nette celým arzenálem užitečných tříd, databázovou vrstvou, atd. Zkuste si schválně jen tak proklikat dokumentaci. Nebo blog. Objevíte spoustu zajímavého.
Ať vám framework přináší spoustu radosti 💙