Hogyan működnek az alkalmazások?
Éppen a Nette dokumentáció alapdokumentumát olvassa. Megtudhatja a webalkalmazások működésének teljes elvét. Szépen A-tól Z-ig, a születés pillanatától a PHP szkript utolsó lélegzetvételéig. Az olvasás után tudni fogja:
- hogyan működik az egész
- mi az a Bootstrap, Presenter és DI konténer
- hogyan néz ki a könyvtárstruktúra
Könyvtárstruktúra
Nyissa meg a WebProject nevű webalkalmazás skeleton példáját, és olvasás közben nézheti azokat a fájlokat, amelyekről szó van.
A könyvtárstruktúra valahogy így néz ki:
web-project/ ├── app/ ← alkalmazás könyvtára │ ├── Core/ ← a működéshez szükséges alaposztályok │ │ └── RouterFactory.php ← URL címek konfigurációja │ ├── Presentation/ ← presenterek, sablonok & társai │ │ ├── @layout.latte ← layout sablon │ │ └── Home/ ← Home presenter könyvtára │ │ ├── HomePresenter.php ← Home presenter osztálya │ │ └── default.latte ← default akció sablonja │ └── Bootstrap.php ← Bootstrap indító osztály ├── bin/ ← parancssorból futtatott szkriptek ├── config/ ← konfigurációs fájlok │ ├── common.neon │ └── services.neon ├── log/ ← naplózott hibák ├── temp/ ← ideiglenes fájlok, cache, … ├── vendor/ ← Composer által telepített könyvtárak │ ├── ... │ └── autoload.php ← az összes telepített csomag autoloadingja ├── www/ ← nyilvános könyvtár vagy a projekt document-rootja │ ├── .htaccess ← mod_rewrite szabályok │ └── index.php ← elsődleges fájl, amellyel az alkalmazás elindul └── .htaccess ← tiltja a hozzáférést minden könyvtárhoz a www kivételével
A könyvtárstruktúrát tetszés szerint módosíthatja, a mappákat átnevezheti vagy áthelyezheti, teljesen rugalmas. A Nette ráadásul okos automatikus felismeréssel rendelkezik, és automatikusan felismeri az alkalmazás helyét, beleértve annak URL alapját is.
Kicsit nagyobb alkalmazásoknál a presenterek és sablonok mappáit alkönyvtárakba tagolhatjuk, az osztályokat pedig névterekbe, amelyeket moduloknak nevezünk.
A www/
könyvtár az ún. nyilvános könyvtár vagy a projekt document-rootja. Átnevezheti anélkül, hogy
bármit is be kellene állítania az alkalmazás oldalán. Csak a hostingot kell konfigurálni
úgy, hogy a document-root erre a könyvtárra mutasson.
A WebProjectet közvetlenül is letöltheti a Nette-vel együtt a Composer segítségével:
composer create-project nette/web-project
Linuxon vagy macOS-en állítsa be a log/
és temp/
könyvtáraknak az írási jogokat.
A WebProject alkalmazás készen áll a futtatásra, egyáltalán semmit nem kell konfigurálni, és azonnal megjelenítheti a
böngészőben a www/
mappához való hozzáféréssel.
HTTP kérés
Minden akkor kezdődik, amikor a felhasználó megnyit egy oldalt a böngészőben. Tehát amikor a böngésző bekopogtat a
szerverhez egy HTTP kéréssel. A kérés egyetlen PHP fájlra irányul, amely a www/
nyilvános könyvtárban
található, és ez az index.php
. Tegyük fel, hogy a kérés a https://example.com/product/123
címre
vonatkozik. A megfelelő szerverbeállításnak köszönhetően
ez az URL is az index.php
fájlra map-elődik, és az végrehajtódik.
Feladata:
- inicializálni a környezetet
- megszerezni a factory-t
- elindítani a Nette alkalmazást, amely kezeli a kérést
Milyen factory-t? Hiszen nem traktorokat gyártunk, hanem weboldalakat! Várjon, mindjárt megmagyarázzuk.
A „környezet inicializálása” alatt például azt értjük, hogy aktiválódik a Tracy, ami egy csodálatos eszköz a naplózáshoz vagy a hibák vizualizálásához. Éles szerveren naplózza a hibákat, fejlesztői szerveren pedig rögtön megjeleníti. Tehát az inicializáláshoz tartozik annak eldöntése is, hogy a web éles vagy fejlesztői módban fut-e. Ehhez a Nette okos automatikus felismerést használ: ha a webet localhoston futtatja, fejlesztői módban fut. Így semmit sem kell konfigurálnia, és az alkalmazás rögtön készen áll mind a fejlesztésre, mind az éles bevetésre. Ezek a lépések végrehajtódnak és részletesen le vannak írva a Bootstrap osztályról szóló fejezetben.
A harmadik pont (igen, a másodikat kihagytuk, de visszatérünk rá) az alkalmazás elindítása. A HTTP kérések
kezelését a Nette-ben a Nette\Application\Application
osztály (továbbiakban Application
) végzi,
tehát amikor azt mondjuk, hogy elindítjuk az alkalmazást, konkrétan ennek az osztálynak az objektumán hívjuk meg a találó
nevű run()
metódust.
A Nette egy mentor, amely a tiszta alkalmazások írására vezeti Önt a bevált módszertanok szerint. És az egyik
leginkább bevált módszertan a dependency injection, röviden DI. Ebben a pillanatban nem akarjuk Önt a DI
magyarázatával terhelni, erre van egy külön fejezet,
a lényeges következmény az, hogy a kulcsfontosságú objektumokat általában egy objektumgyár hozza létre nekünk, amelyet
DI konténernek (röviden DIC) neveznek. Igen, ez az a factory, amelyről az előbb szó volt. És ez gyártja nekünk az
Application
objektumot is, ezért először a konténerre van szükségünk. A Configurator
osztály
segítségével szerezzük meg, és hagyjuk, hogy létrehozza az Application
objektumot, meghívjuk rajta a
run()
metódust, és ezzel elindul a Nette alkalmazás. Pontosan ez történik az index.php fájlban.
Nette Application
Az Application osztálynak egyetlen feladata van: válaszolni a HTTP kérésre.
A Nette-ben írt alkalmazások sok ún. presenter-re tagolódnak (más keretrendszerekben találkozhat a controller kifejezéssel, ez ugyanaz), amelyek olyan osztályok, amelyek mindegyike egy konkrét weboldalt képvisel: pl. a kezdőlapot; egy terméket a webáruházban; a bejelentkezési űrlapot; a sitemap feedet stb. Az alkalmazásnak egytől több ezer presenterig terjedhet a száma.
Az Application azzal kezdi, hogy megkéri az ún. routert, hogy döntse el, melyik presenternek adja át az aktuális kérést
feldolgozásra. A router eldönti, kié a felelősség. Megnézi a bemeneti URL-t https://example.com/product/123
,
és attól függően, hogyan van beállítva, eldönti, hogy ez például a Product
presenter munkája,
amelytől akcióként a termék megjelenítését (show
) kéri id: 123
-mal. A presenter + akció
párt jó szokás kettősponttal elválasztva írni, mint Product:show
.
Tehát a router átalakította az URL-t egy Presenter:action
párra + paraméterekre, esetünkben
Product:show
+ id: 123
. Hogy néz ki egy ilyen router, megnézheti az
app/Core/RouterFactory.php
fájlban, és részletesen leírjuk a Routing fejezetben.
Menjünk tovább. Az Application már ismeri a presenter nevét, és folytathatja. Azzal, hogy létrehozza a
ProductPresenter
osztály objektumát, ami a Product
presenter kódja. Pontosabban szólva, megkéri a
DI konténert, hogy hozza létre a presentert, mert a gyártás az ő feladata.
A presenter például így nézhet ki:
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ProductRepository $repository,
) {
}
public function renderShow(int $id): void
{
// adatokat szerzünk a modellből és átadjuk a sablonnak
$this->template->product = $this->repository->getProduct($id);
}
}
A kérés feldolgozását a presenter veszi át. És a feladat világos: hajtsa végre a show
akciót
id: 123
-mal. Ami a presenterek nyelvén azt jelenti, hogy meghívódik a renderShow()
metódus, és a
$id
paraméterben megkapja a 123
-at.
A presenter több akciót is kezelhet, tehát több render<Action>()
metódusa lehet. De javasoljuk olyan
presenterek tervezését, amelyeknek egy vagy a lehető legkevesebb akciója van.
Tehát meghívódott a renderShow(123)
metódus, amelynek kódja ugyan kitalált példa, de láthatja rajta,
hogyan adunk át adatokat a sablonnak, azaz a $this->template
-be írással.
Ezután a presenter visszaadja a választ. Ez lehet egy HTML oldal, egy kép, egy XML dokumentum, egy fájl elküldése a
lemezről, JSON, vagy akár átirányítás egy másik oldalra. Fontos, hogy ha explicit módon nem mondjuk meg, hogyan
válaszoljon (ami a ProductPresenter
esete), akkor a válasz egy HTML oldalt tartalmazó sablon renderelése lesz.
Miért? Mert az esetek 99%-ában sablont szeretnénk renderelni, ezért a presenter ezt a viselkedést veszi alapértelmezettnek,
és meg akarja könnyíteni a munkánkat. Ez a Nette lényege.
Még azt sem kell megadnunk, hogy melyik sablont renderelje, az útvonalat maga vezeti le. A show
akció esetében
egyszerűen megpróbálja betölteni a show.latte
sablont a ProductPresenter
osztályt tartalmazó
könyvtárban. Ugyanígy megpróbálja megtalálni a layoutot az @layout.latte
fájlban (részletesebben a sablonok kereséséről).
És ezután rendereli a sablonokat. Ezzel a presenter és az egész alkalmazás feladata befejeződött, és a mű elkészült. Ha a sablon nem létezne, 404-es hibaoldal jelenne meg. Többet a presenterekről a Presenterek oldalon olvashat.
Biztonság kedvéért próbáljuk meg összefoglalni az egész folyamatot egy kicsit más URL-lel:
- Az URL
https://example.com
lesz - Indítjuk az alkalmazást, létrejön a konténer és elindul az
Application::run()
- A router dekódolja az URL-t
Home:default
párként - Létrejön a
HomePresenter
osztály objektuma - Meghívódik a
renderDefault()
metódus (ha létezik) - Renderelődik a sablon, pl.
default.latte
a layouttal, pl.@layout.latte
Talán most sok új fogalommal találkozott, de reméljük, hogy van értelmük. Alkalmazások fejlesztése a Nette-ben óriási kényelem.
Sablonok
Ha már szóba kerültek a sablonok, a Nette a Latte sablonrendszert használja.
Ezért is vannak a .latte
kiterjesztések a sablonoknál. A Latte-t egyrészt azért használják, mert ez a
legbiztonságosabb sablonrendszer PHP-hoz, másrészt pedig a legintuitívabb rendszer. Nem kell sok újat tanulnia, elegendő a
PHP ismerete és néhány tag. Mindent megtudhat a
dokumentációban.
A sablonban linkeket hozunk létre más presenterekhez és akciókhoz így:
<a n:href="Product:show $productId">termék részletei</a>
Egyszerűen a valós URL helyett írja be az ismert Presenter:action
párt, és adja meg az esetleges
paramétereket. A trükk az n:href
-ben van, amely azt mondja, hogy ezt az attribútumot a Nette dolgozza fel. És
generálja:
<a href="/product/456">termék részletei</a>
Az URL generálását a már korábban említett router végzi. Ugyanis a Nette routerei kivételesek abban, hogy nemcsak az URL-ből tudnak átalakítást végezni presenter:action párra, hanem fordítva is, azaz a presenter nevéből + akcióból + paraméterekből URL-t generálni. Ennek köszönhetően a Nette-ben teljesen megváltoztathatja az URL-ek formáját egy kész alkalmazásban anélkül, hogy egyetlen karaktert is megváltoztatna a sablonban vagy a presenterben. Csak a router módosításával. Ennek köszönhetően működik az ún. kanonizáció is, ami a Nette egy másik egyedülálló tulajdonsága, amely hozzájárul a jobb SEO-hoz (keresőoptimalizálás) azáltal, hogy automatikusan megakadályozza a duplikált tartalom létezését különböző URL-eken. Sok programozó ezt lenyűgözőnek tartja.
Interaktív komponensek
A presenterekről még egy dolgot el kell árulnunk: beépített komponensrendszerük van. Valami hasonlót a Delphi vagy az ASP.NET Web Forms ismerői ismerhetnek, valami távolról hasonlóra épül a React vagy a Vue.js is. A PHP keretrendszerek világában ez teljesen egyedülálló dolog.
A komponensek önálló, újrafelhasználható egységek, amelyeket oldalakba (azaz presenterekbe) illesztünk be. Lehetnek űrlapok, datagrid-ek, menük, szavazófelületek, valójában bármi, amit érdemes ismételten használni. Létrehozhatunk saját komponenseket, vagy használhatunk néhányat a hatalmas kínálatból származó nyílt forráskódú komponensek közül.
A komponensek alapvetően befolyásolják az alkalmazásfejlesztési megközelítést. Új lehetőségeket nyitnak meg az oldalak előre elkészített egységekből való összeállítására. És ráadásul van valami közös bennük a Hollywooddal.
DI konténer és konfiguráció
A DI konténer vagy objektumgyár az egész alkalmazás szíve.
Ne aggódjon, ez nem egy varázslatos fekete doboz, ahogy talán az előző sorokból tűnhetett. Valójában ez egy
meglehetősen unalmas PHP osztály, amelyet a Nette generál és a cache könyvtárba ment. Rengeteg
createServiceAbcd()
nevű metódusa van, és mindegyik tud létrehozni és visszaadni valamilyen objektumot. Igen,
van ott egy createServiceApplication()
metódus is, amely létrehozza a Nette\Application\Application
-t,
amire szükségünk volt az index.php
fájlban az alkalmazás elindításához. És vannak metódusok, amelyek az
egyes presentereket gyártják. És így tovább.
Azokat az objektumokat, amelyeket a DI konténer létrehoz, valamilyen okból szolgáltatásoknak nevezik.
Ami ebben az osztályban igazán különleges, az az, hogy nem Ön programozza, hanem a keretrendszer. Valóban PHP kódot
generál és elmenti a lemezre. Ön csak utasításokat ad, hogy milyen objektumokat tudjon a konténer gyártani és pontosan
hogyan. És ezek az utasítások a konfigurációs fájlokban vannak leírva,
amelyekhez a NEON formátumot használják, és ezért .neon
kiterjesztésük van.
A konfigurációs fájlok tisztán a DI konténer instruálására szolgálnak. Tehát ha például a session szekcióban megadom az expiration: 14 days
opciót, akkor a DI konténer a sessiont reprezentáló Nette\Http\Session
objektum létrehozásakor meghívja annak
setExpiration('14 days')
metódusát, és ezzel a konfiguráció valósággá válik.
Van itt Önnek egy egész fejezet, amely leírja, mit lehet konfigurálni és hogyan lehet saját szolgáltatásokat definiálni.
Amint egy kicsit belemerül a szolgáltatások létrehozásába, találkozni fog az autowiring szóval. Ez egy olyan trükk, amely hihetetlenül leegyszerűsíti az életét. Képes automatikusan átadni az objektumokat oda, ahol szüksége van rájuk (például az osztályai konstruktoraiban), anélkül, hogy bármit is tennie kellene. Rájön majd, hogy a Nette DI konténere egy kis csoda.
Merre tovább?
Áttekintettük a Nette alkalmazások alapelveit. Eddig nagyon felületesen, de hamarosan mélyebbre hatol, és idővel csodálatos webalkalmazásokat fog létrehozni. Merre tovább? Kipróbálta már az Első alkalmazás írása tutorialt?
A fent leírtakon kívül a Nette egész arzenáljával rendelkezik hasznos osztályoknak, adatbázis rétegnek, stb. Próbálja meg csak úgy átkattintgatni a dokumentációt. Vagy a blogot. Sok érdekes dolgot fog felfedezni.
Hozzon a keretrendszer sok örömet Önnek 💙