Kako delujejo aplikacije?

Pravkar berete osnovno listino dokumentacije Nette. Spoznali boste celoten princip delovanja spletnih aplikacij. Lepo od A do Ž, od trenutka nastanka do zadnjega izdiha skripta PHP. Po branju boste vedeli:

  • kako vse skupaj deluje
  • kaj so Bootstrap, Presenter in DI vsebnik
  • kako izgleda struktura map

Struktura map

Odpri si primer ogrodja spletne aplikacije imenovane WebProject in med branjem lahko gledaš datoteke, o katerih je govora.

Struktura map izgleda nekako takole:

web-project/
├── app/                      ← mapa z aplikacijo
│   ├── Core/                 ← osnovni razredi, potrebni za delovanje
│   │   └── RouterFactory.php ← konfiguracija URL naslovov
│   ├── Presentation/         ← presenterji, predloge & co.
│   │   ├── @layout.latte     ← predloga postavitve
│   │   └── Home/             ← mapa presenterja Home
│   │       ├── HomePresenter.php ← razred presenterja Home
│   │       └── default.latte ← predloga akcije default
│   └── Bootstrap.php         ← zagonski razred Bootstrap
├── bin/                      ← skripti, zagnani iz ukazne vrstice
├── config/                   ← konfiguracijske datoteke
│   ├── common.neon
│   └── services.neon
├── log/                      ← zabeležene napake
├── temp/                     ← začasne datoteke, predpomnilnik, …
├── vendor/                   ← knjižnice, nameščene s Composerjem
│   ├── ...
│   └── autoload.php          ← samodejno nalaganje vseh nameščenih paketov
├── www/                      ← javna mapa ali document-root projekta
│   ├── .htaccess             ← pravila mod_rewrite
│   └── index.php             ← začetna datoteka, s katero se aplikacija zažene
└── .htaccess                 ← prepoveduje dostop do vseh map razen www

Strukturo map lahko kakorkoli spreminjate, mape preimenujete ali premaknete, je popolnoma fleksibilna. Nette poleg tega razpolaga s pametnim samodejnim zaznavanjem in samodejno prepozna lokacijo aplikacije, vključno z njeno osnovno URL.

Pri nekoliko večjih aplikacijah lahko mape s presenterji in predlogami razčlenimo v podmape in razrede v imenske prostore, ki jim rečemo moduli.

Mapa www/ predstavlja t.i. javno mapo ali document-root projekta. Lahko jo preimenujete brez potrebe po kakršnemkoli dodatnem nastavljanju na strani aplikacije. Le potrebno je konfigurirati gostovanje tako, da document-root kaže v to mapo.

WebProject si lahko tudi takoj prenesete vključno z Nette in to s pomočjo Composerja:

composer create-project nette/web-project

Na Linuxu ali macOS nastavite mapama log/ in temp/ pravice za pisanje.

Aplikacija WebProject je pripravljena za zagon, ni treba ničesar konfigurirati in jo lahko takoj prikažete v brskalniku z dostopom do mape www/.

HTTP zahtevek

Vse se začne v trenutku, ko uporabnik v brskalniku odpre stran. Torej, ko brskalnik potrka na strežnik s HTTP zahtevkom. Zahtevek cilja na eno samo PHP datoteko, ki se nahaja v javni mapi www/, in to je index.php. Recimo, da gre za zahtevek na naslov https://example.com/product/123. Zahvaljujoč primerni nastavitvi strežnika se tudi ta URL preslika na datoteko index.php in ta se izvede.

Njegova naloga je:

  1. inicializirati okolje
  2. pridobiti tovarno
  3. zagnati Nette aplikacijo, ki bo obdelala zahtevek

Kakšno tovarno? Saj ne izdelujemo traktorjev, ampak spletne strani! Počakajte, takoj se bo pojasnilo.

Z besedami »inicializacija okolja« mislimo na primer to, da se aktivira Tracy, kar je čudovito orodje za beleženje ali vizualizacijo napak. Na produkcijskem strežniku napake beleži, na razvojnem jih takoj prikaže. Zato k inicializaciji spada tudi odločitev, ali spletno mesto teče v produkcijskem ali razvojnem načinu. Za to Nette uporablja pametno samodejno zaznavanje: če spletno mesto zaženete na localhostu, teče v razvojnem načinu. Ni vam treba ničesar konfigurirati in aplikacija je takoj pripravljena tako za razvoj kot za ostro uporabo. Ti koraki se izvajajo in so podrobno opisani v poglavju o razredu Bootstrap.

Tretja točka (da, drugo smo preskočili, a se bomo vrnili k njej) je zagon aplikacije. Obdelavo HTTP zahtevkov ima v Nette na skrbi razred Nette\Application\Application (v nadaljevanju Application), zato ko rečemo zagnati aplikacijo, mislimo konkretno na klicanje metode s primernim imenom run() na objektu tega razreda.

Nette je mentor, ki vas vodi k pisanju čistih aplikacij po preverjenih metodologijah. In ena od tistih popolnoma najbolj preverjenih se imenuje dependency injection, skrajšano DI. V tem trenutku vas ne želimo obremenjevati z razlago DI, za to je tu ločeno poglavje, bistven je posledica, da nam bo ključne objekte običajno ustvarjala tovarna objektov, ki se ji reče DI vsebnik (skrajšano DIC). Da, to je tista tovarna, o kateri je bila pred kratkim govora. In izdelala nam bo tudi objekt Application, zato potrebujemo najprej vsebnik. Pridobimo ga s pomočjo razreda Configurator in mu pustimo izdelati objekt Application, na njem pokličemo metodo run() in s tem se zažene Nette aplikacija. Točno to se dogaja v datoteki index.php.

Nette Application

Razred Application ima eno samo nalogo: odgovoriti na HTTP zahtevek.

Aplikacije, napisane v Nette, se členijo v veliko t.i. presenterjev (v drugih ogrodjih se lahko srečate z izrazom controller, gre za isto stvar), kar so razredi, od katerih vsak predstavlja neko konkretno stran spletnega mesta: npr. domačo stran; izdelek v spletni trgovini; prijavni obrazec; sitemap vir itd. Aplikacija lahko ima od enega do tisoč presenterjev.

Application začne tako, da prosi t.i. usmerjevalnik (router), naj odloči, kateremu od presenterjev predati trenutni zahtevek v obdelavo. Usmerjevalnik odloči, čigava je to odgovornost. Pogleda vhodni URL https://example.com/product/123 in na podlagi tega, kako je nastavljen, odloči, da je to delo npr. za presenter Product, od katerega bo želel kot akcijo prikaz (show) izdelka z id: 123. Par presenter + akcija je dobra navada zapisovati ločeno z dvopičjem kot Product:show.

Torej je usmerjevalnik transformiral URL v par Presenter:action + parametri, v našem primeru Product:show + id: 123. Kako takšen usmerjevalnik izgleda, si lahko pogledate v datoteki app/Core/RouterFactory.php in ga podrobno opisujemo v poglavju Usmerjanje.

Pojdimo naprej. Application že pozna ime presenterja in lahko nadaljuje naprej. S tem, da izdela objekt razreda ProductPresenter, kar je koda presenterja Product. Natančneje rečeno, prosi DI vsebnik, naj presenter izdela, ker je za izdelovanje tu on.

Presenter lahko izgleda na primer takole:

class ProductPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ProductRepository $repository,
	) {
	}

	public function renderShow(int $id): void
	{
		// pridobimo podatke iz modela in jih posredujemo predlogi
		$this->template->product = $this->repository->getProduct($id);
	}
}

Obdelavo zahtevka prevzame presenter. In naloga je jasna: izvedi akcijo show z id: 123. Kar v jeziku presenterjev pomeni, da se pokliče metoda renderShow() in v parametru $id dobi 123.

Presenter lahko obravnava več akcij, torej ima več metod render<Akcija>(). Vendar priporočamo načrtovanje presenterjev z eno ali čim manj akcijami.

Torej, poklicala se je metoda renderShow(123), katere koda je sicer izmišljen primer, vendar lahko na njej vidite, kako se posredujejo podatki v predlogo, torej z zapisom v $this->template.

Nato presenter vrne odgovor. Ta je lahko HTML stran, slika, XML dokument, pošiljanje datoteke z diska, JSON ali pa preusmeritev na drugo stran. Pomembno je, da če eksplicitno ne povemo, kako naj odgovori (kar je primer ProductPresenter), bo odgovor izris predloge s HTML stranjo. Zakaj? Ker v 99 % primerov želimo izrisati predlogo, zato presenter to obnašanje jemlje kot privzeto in nam želi olajšati delo. To je smisel Nette.

Ni nam treba niti navajati, katero predlogo izrisati, pot do nje si izpelje sam. V primeru akcije show preprosto poskusi naložiti predlogo show.latte v mapi z razredom ProductPresenter. Prav tako poskusi poiskati postavitev v datoteki @layout.latte (podrobneje o iskanju predlog).

In nato predloge izriše. S tem je naloga presenterja in celotne aplikacije končana in delo je zaključeno. Če predloga ne bi obstajala, se vrne stran z napako 404. Več o presenterjih preberite na strani Presenterji.

Za vsak slučaj, poskusimo si ponoviti celoten proces z nekoliko drugačnim URL-jem:

  1. URL bo https://example.com
  2. zaženemo aplikacijo, ustvari se vsebnik in zažene Application::run()
  3. usmerjevalnik dekodira URL kot par Home:default
  4. ustvari se objekt razreda HomePresenter
  5. pokliče se metoda renderDefault() (če obstaja)
  6. izriše se predloga npr. default.latte s postavitvijo npr. @layout.latte

Morda ste se zdaj srečali z veliko novimi pojmi, vendar verjamemo, da imajo smisel. Ustvarjanje aplikacij v Nette je izjemno prijetno.

Predloge

Ko smo že pri predlogah, v Nette se uporablja sistem predlog Latte. Zato tudi končnice .latte pri predlogah. Latte se uporablja delsno zato, ker gre za najbolj varen sistem predlog za PHP, in hkrati tudi najbolj intuitiven sistem. Ni se vam treba učiti veliko novega, zadostuje znanje PHP in nekaj značk. Vse boste izvedeli v dokumentaciji.

V predlogi se ustvarjajo povezave na druge presenterje & akcije takole:

<a n:href="Product:show $productId">podrobnosti izdelka</a>

Preprosto namesto realnega URL-ja napišete znani par Presenter:action in navedete morebitne parametre. Trik je v n:href, ki pravi, da ta atribut obdela Nette. In generira:

<a href="/product/456">podrobnosti izdelka</a>

Generiranje URL-jev ima na skrbi že prej omenjeni usmerjevalnik. Namreč usmerjevalniki v Nette so izjemni s tem, da znajo izvajati ne samo transformacije iz URL-ja v par presenter:action, ampak tudi obratno, torej iz imena presenterja + akcije + parametrov generirati URL. Zahvaljujoč temu lahko v Nette popolnoma spremenite oblike URL-jev v celotni končani aplikaciji, ne da bi spremenili en sam znak v predlogi ali presenterju. Samo s tem, da uredite usmerjevalnik. Prav tako zahvaljujoč temu deluje t.i. kanonizacija, kar je še ena edinstvena lastnost Nette, ki prispeva k boljšemu SEO (optimizaciji najdljivosti na internetu) s tem, da samodejno preprečuje obstoj podvojene vsebine na različnih URL-jih. Veliko programerjev to šteje za osupljivo.

Interaktivne komponente

O presenterjih vam moramo povedati še eno stvar: imajo vgrajen komponentni sistem. Nekaj podobnega se lahko spomnijo veterani iz Delphi ali ASP.NET Web Forms, na nečem oddaljeno podobnem temeljita React ali Vue.js. V svetu PHP ogrodij gre za popolnoma edinstveno zadevo.

Komponente so samostojne ponovno uporabne celote, ki jih vstavljamo v strani (torej presenterje). Lahko so obrazci, podatkovne mreže, meniji, glasovalne ankete, pravzaprav karkoli, kar ima smisel uporabljati večkrat. Lahko ustvarjamo lastne komponente ali uporabljamo nekatere iz ogromne ponudbe odprtokodnih komponent.

Komponente bistveno vplivajo na pristop k ustvarjanju aplikacij. Odprle vam bodo nove možnosti sestavljanja strani iz vnaprej pripravljenih enot. In poleg tega imajo nekaj skupnega s Hollywoodom.

DI vsebnik in konfiguracija

DI vsebnik ali tovarna objektov je srce celotne aplikacije.

Ne skrbite, ni to nobena čarobna črna škatla, kot bi se morda lahko zdelo iz prejšnjih vrstic. Pravzaprav je to ena precej dolgočasna PHP klasa, ki jo generira Nette in shrani v mapo s predpomnilnikom. Ima veliko metod, poimenovanih kot createServiceAbcd(), in vsaka od njih zna izdelati in vrniti nek objekt. Da, tam je tudi metoda createServiceApplication(), ki izdela Nette\Application\Application, ki smo ga potrebovali v datoteki index.php za zagon aplikacije. In tam so metode, ki izdelujejo posamezne presenterje. In tako naprej.

Objektom, ki jih DI vsebnik ustvarja, se iz nekega razloga reče storitve.

Kar je pri tem razredu resnično posebno, je to, da ga ne programirate vi, ampak ogrodje. On dejansko generira PHP kodo in jo shrani na disk. Vi samo dajete navodila, kakšne objekte naj zna vsebnik izdelovati in kako natančno. In ta navodila so zapisana v konfiguracijskih datotekah, za katere se uporablja format NEON in zato imajo tudi končnico .neon.

Konfiguracijske datoteke služijo izključno za navodila DI vsebnika. Torej, ko na primer navedem v sekciji session možnost expiration: 14 days, DI vsebnik pri ustvarjanju objekta Nette\Http\Session, ki predstavlja sejo, pokliče njegovo metodo setExpiration('14 days') in s tem konfiguracija postane resničnost.

Tu je za vas pripravljeno celo poglavje, ki opisuje, kaj vse je mogoče konfigurirati in kako definirati lastne storitve.

Ko se malo poglobite v ustvarjanje storitev, boste naleteli na besedo autowiring. To je iznajdba, ki vam bo na neverjeten način poenostavila življenje. Zna samodejno posredovati objekte tja, kjer jih potrebujete (na primer v konstruktorjih vaših razredov), ne da bi morali karkoli narediti. Ugotovili boste, da je DI vsebnik v Nette mali čudež.

Kam naprej?

Prešli smo osnovne principe aplikacij v Nette. Zaenkrat zelo površno, vendar boste kmalu prodrli v globino in sčasoma ustvarili čudovite spletne aplikacije. Kam nadaljevati? Ste že preizkusili vadnico Pišemo prvo aplikacijo?

Poleg zgoraj opisanega Nette razpolaga s celim arzenalom uporabnih razredov, podatkovno plastjo, itd. Poskusite si samo tako preklikati dokumentacijo. Ali blog. Odkrili boste veliko zanimivega.

Naj vam ogrodje prinese veliko veselja 💙

različica: 4.0