Prezentatori
Vom învăța cum să scriem prezentări și șabloane în Nette. După ce veți citi veți ști:
- cum funcționează prezentatorul
- ce sunt parametrii persistenți
- cum se redă un șablon
Știm deja că un prezentator este o clasă care reprezintă o anumită pagină a unei aplicații web, cum ar fi o pagină de pornire; un produs în magazinul electronic; un formular de înregistrare; un feed de hartă a site-ului etc. Aplicația poate avea de la unul până la mii de prezentatori. În alte framework-uri, aceștia sunt cunoscuți și sub denumirea de controlori.
De obicei, termenul de prezentator se referă la un descendent al clasei Nette\Application\UI\Presenter, care este potrivită pentru interfețele web și pe care o vom discuta în restul acestui capitol. În sens general, un prezentator este orice obiect care implementează interfața Nette\Application\IPresenter.
Ciclul de viață al unui prezentator
Sarcina prezentatorului este de a procesa cererea și de a returna un răspuns (care poate fi o pagină HTML, o imagine, o redirecționare etc.).
Așadar, la început este o cerere. Nu este direct o cerere HTTP, ci un obiect Nette\Application\Request în care a fost transformată cererea HTTP cu ajutorul unui router. De obicei, nu intrăm în contact cu acest obiect, deoarece prezentatorul deleagă în mod inteligent procesarea cererii către metode speciale, pe care le vom vedea acum.
Ciclul de viață al prezentatorului
Figura prezintă o listă de metode care sunt apelate secvențial de sus în jos, dacă există. Niciuna dintre ele nu trebuie să existe neapărat, putem avea un presenter complet gol, fără nicio metodă, și să construim un simplu web static pe el.
__construct()
Constructorul nu face parte exact din ciclul de viață al prezentatorului, deoarece este apelat în momentul creării obiectului. Dar îl menționăm datorită importanței sale. Constructorul (împreună cu metoda inject) este utilizat pentru a trece dependențele.
Prezentatorul nu trebuie să se ocupe de logica de afaceri a aplicației, să scrie și să citească din baza de date, să
efectueze calcule etc. Aceasta este sarcina pentru clasele dintr-un strat, pe care îl numim model. De exemplu, clasa
ArticleRepository
poate fi responsabilă de încărcarea și salvarea articolelor. Pentru ca prezentatorul să
o folosească, aceasta este transmisă folosind
injecția de dependență:
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articles,
) {
}
}
startup()
Imediat după primirea cererii, se invocă metoda startup ()
. Puteți să o utilizați pentru a inițializa
proprietățile, a verifica privilegiile utilizatorilor etc. Este necesar să se apeleze întotdeauna strămoșul
parent::startup()
.
action<Action>(args...)
Similar cu metoda render<View>()
. În timp ce render<View>()
este destinată să
pregătească datele pentru un anumit șablon, care este ulterior redat, în action<Action>()
o solicitare
este procesată fără a fi urmată de redarea șablonului. De exemplu, datele sunt procesate, un utilizator este logat sau
deconectat și așa mai departe, iar apoi se redirecționează în altă parte.
Este important ca action<Action>()
este apelat înainte de render<View>()
, astfel încât
în interiorul acesteia să putem eventual să schimbăm următorul curs al ciclului de viață, adică să schimbăm șablonul
care va fi redat și, de asemenea, metoda render<View>()
care va fi apelată, folosind
setView('otherView')
.
Parametrii din cerere sunt trecuți către metodă. Este posibil și recomandat să se precizeze tipurile pentru parametri, de
exemplu actionShow(int $id, string $slug = null)
– dacă parametrul id
lipsește sau dacă nu este un
număr întreg, prezentatorul returnează eroarea 404 și încheie operațiunea.
handle<Signal>(args...)
Această metodă procesează așa-numitele semnale, pe care le vom discuta în capitolul despre componente. Ea este destinată în principal componentelor și procesării cererilor AJAX.
Parametrii sunt pasați metodei, ca în cazul lui action<Action>()
, inclusiv verificarea tipului.
beforeRender()
Metoda beforeRender
, după cum sugerează și numele, este apelată înainte de fiecare metodă
render<View>()
. Este utilizată pentru configurarea obișnuită a șabloanelor, trecerea variabilelor pentru
aspect și așa mai departe.
render<View>(args...)
Locul în care pregătim șablonul pentru redarea ulterioară, îi transmitem date etc.
Parametrii sunt trecuți la metodă, ca în cazul lui action<Action>()
, inclusiv verificarea tipului.
public function renderShow(int $id): void
{
// obținem datele din model și le transmitem șablonului
$this->template->article = $this->articles->getById($id);
}
afterRender()
Metoda afterRender
, după cum sugerează și numele, este apelată după fiecare render<View>()
metodă. Ea este utilizată destul de rar.
shutdown()
Este apelată la sfârșitul ciclului de viață al prezentatorului.
Bun sfat înainte de a merge mai departe. După cum vedeți, prezentatorul poate gestiona mai multe acțiuni/viziuni,
adică poate avea mai multe metode. render<View>()
. Dar recomandăm proiectarea de prezentatoare cu una sau
cât mai puține acțiuni.
Trimiterea unui răspuns
Răspunsul prezentatorului este, de obicei, redarea șablonului cu pagina HTML, dar poate fi, de asemenea, trimiterea unui fișier, JSON sau chiar redirecționarea către o altă pagină.
În orice moment în timpul ciclului de viață, puteți utiliza oricare dintre următoarele metode pentru a trimite un răspuns și a ieși din prezentator în același timp:
redirect()
,redirectPermanent()
,redirectUrl()
șiforward()
redirecționări.error()
părăsește prezentatorul din cauza unei erorisendJson($data)
părăsește prezentatorul și trimite datele în format JSONsendTemplate()
părăsește prezentatorul și redă imediat șablonulsendResponse($response)
părăsește prezentatorul și trimite propriul răspunsterminate()
părăsește prezentatorul fără răspuns
Dacă nu apelați niciuna dintre aceste metode, prezentatorul va proceda automat la redarea șablonului. De ce? Ei bine, pentru că în 99% din cazuri dorim să desenăm un șablon, așa că prezentatorul ia acest comportament ca fiind implicit și dorește să ne ușureze munca.
Crearea legăturilor
Prezentatorul are o metodă link()
, care este utilizată pentru a crea legături URL către alți prezentatori.
Primul parametru este prezentatorul și acțiunea țintă, urmat de argumente, care pot fi transmise sub formă de matrice:
$url = $this->link('Product:show', $id);
$url = $this->link('Product:show', [$id, 'lang' => 'en']);
În șablon, creăm legături către alți prezentatori și acțiuni după cum urmează:
<a n:href="Product:show $id">product detail</a>
Pur și simplu scrieți perechea cunoscută Presenter:action
în locul URL-ului real și includeți orice
parametru. Trucul este n:href
, care spune că acest atribut va fi procesat de Latte și generează un URL real. În
Nette, nu trebuie să vă gândiți deloc la URL-uri, ci doar la prezentatori și acțiuni.
Pentru mai multe informații, consultați Crearea de legături.
Redirecționare
Metodele redirect()
și forward()
sunt utilizate pentru a trece la un alt prezentator, care au
o sintaxă foarte asemănătoare cu cea a metodei link().
Metoda forward()
trece imediat la noul prezentator, fără redirecționare HTTP:
$this->forward('Product:show');
Exemplu de redirecționare temporară cu codul HTTP 302 sau 303:
$this->redirect('Product:show', $id);
Pentru a obține o redirecționare permanentă cu codul HTTP 301, utilizați:
$this->redirectPermanent('Product:show', $id);
Puteți redirecționa către un alt URL din afara aplicației cu metoda redirectUrl()
:
$this->redirectUrl('https://nette.org');
Redirecționarea încheie imediat ciclul de viață al prezentatorului prin aruncarea așa-numitei excepții de terminare
silențioasă Nette\Application\AbortException
.
Înainte de redirecționare, este posibil să se trimită un mesaj flash, mesaje care vor fi afișate în șablon după redirecționare.
Mesaje flash
Acestea sunt mesaje care, de obicei, informează despre rezultatul unei operații. O caracteristică importantă a mesajelor flash este că acestea sunt disponibile în șablon chiar și după redirecționare. Chiar și după ce au fost afișate, ele vor rămâne în viață încă 30 de secunde – de exemplu, în cazul în care utilizatorul ar reîmprospăta involuntar pagina – mesajul nu se va pierde.
Trebuie doar să apelați metoda flashMessage() și
presenter se va ocupa de transmiterea mesajului către șablon. Primul argument este textul mesajului, iar al doilea argument
opțional este tipul acestuia (error, warning, info etc.). Metoda flashMessage()
returnează o instanță de mesaj
flash, pentru a ne permite să adăugăm mai multe informații.
$this->flashMessage('Item was removed.');
$this->redirect(/* ... */);
În șablon, aceste mesaje sunt disponibile în variabila $flashes
sub forma unor obiecte stdClass
,
care conțin proprietățile message
(textul mesajului), type
(tipul mesajului) și pot conține
informațiile despre utilizator deja menționate. Le desenăm după cum urmează:
{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
Eroare 404 etc.
Atunci când nu putem îndeplini cererea deoarece, de exemplu, articolul pe care dorim să îl afișăm nu există în baza de
date, vom arunca eroarea 404 folosind metoda error(string $message = null, int $httpCode = 404)
, care reprezintă
eroarea HTTP 404:
public function renderShow(int $id): void
{
$article = $this->articles->getById($id);
if (!$article) {
$this->error();
}
// ...
}
Codul de eroare HTTP poate fi trecut ca al doilea parametru, valoarea implicită fiind 404. Metoda funcționează prin
aruncarea excepției Nette\Application\BadRequestException
, după care Application
transmite controlul
către prezentatorul de erori. Care este un prezentator a cărui sarcină este de a afișa o pagină de informare cu privire la
eroare. Error-preseter este setat în configurația
aplicației.
Trimiterea JSON
Exemplu de metodă de acțiune care trimite date în format JSON și iese din prezentator:
public function actionData(): void
{
$data = ['hello' => 'nette'];
$this->sendJson($data);
}
Parametrii persistenți
Parametrii persistenți sunt transferați automat în legături. Acest lucru înseamnă că nu trebuie să îi
specificăm în mod explicit în fiecare link()
sau n:href
din șablon, dar ei vor fi totuși
transferați.
Dacă aplicația dvs. are mai multe versiuni lingvistice, atunci limba curentă este un parametru care trebuie să facă
întotdeauna parte din URL. Și ar fi incredibil de obositor să îl menționați în fiecare link. Acest lucru nu este necesar cu
Nette. Pur și simplu marcăm parametrul lang
ca fiind persistent în acest mod:
class ProductPresenter extends Nette\Application\UI\Presenter
{
/** @persistent */
public $lang;
}
Dacă valoarea curentă a parametrului lang
este 'en'
, atunci URL-ul creat cu link()
sau
n:href
în șablon va conține lang=en
. Minunat!
Cu toate acestea, putem adăuga și parametrul lang
și, prin aceasta, putem schimba valoarea acestuia:
<a n:href="Product:show $id, lang: en">detail in English</a>
Sau, dimpotrivă, poate fi eliminat prin setarea la null:
<a n:href="Product:show $id, lang: null">click here</a>
Variabila persistentă trebuie să fie declarată ca fiind publică. De asemenea, putem specifica o valoare implicită. Dacă parametrul are aceeași valoare ca cea implicită, acesta nu va fi inclus în URL.
Persistența reflectă ierarhia claselor de prezentatori, astfel încât parametrul definit într-un anumit prezentator sau trăsătură este apoi transferat automat la fiecare prezentator care moștenește din acesta sau care utilizează aceeași trăsătură.
În PHP 8, puteți utiliza, de asemenea, atribute pentru a marca parametrii persistenți:
use Nette\Application\Attributes\Persistent;
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public $lang;
}
Componente interactive
Prezentatorii au un sistem de componente încorporat. Componentele sunt unități separate reutilizabile pe care le introducem în prezentatoare. Ele pot fi formulare, datagrids, meniuri, de fapt orice lucru care are sens să fie folosit în mod repetat.
Cum se plasează componentele și cum sunt utilizate ulterior în prezentator? Acest lucru este explicat în capitolul Componente. Veți afla chiar și ce legătură au acestea cu Hollywood.
De unde pot obține niște componente? La pagina Componette puteți găsi câteva componente open-source și alte add-on-uri pentru Nette, care sunt realizate și partajate de comunitatea Nette Framework.
Aprofundare
Ceea ce am arătat până acum în acest capitol va fi probabil suficient. Rândurile următoare sunt destinate celor care sunt interesați de prezentatori în profunzime și doresc să știe totul.
Cerință și parametri
Cererea gestionată de prezentator este obiectul Nette\Application\Request și este returnată
de metoda prezentatorului getRequest()
. Acesta include o matrice de parametri și fiecare dintre ei aparține fie
unora dintre componente, fie direct prezentatorului (care este de fapt tot o componentă, deși una specială). Așadar, Nette
redistribui parametrii și trece între componentele individuale (și prezentator) prin apelarea metodei
loadState(array $params)
, care este descrisă în continuare în capitolul Componente. Parametrii pot fi obținuți prin metoda
getParameters(): array
, folosind individual getParameter($name)
. Valorile parametrilor sunt șiruri de
caractere sau array-uri de șiruri de caractere, acestea fiind practic date brute obținute direct dintr-un URL.
Salvarea și restaurarea cererii
Puteți salva cererea curentă într-o sesiune sau o puteți restaura din sesiune și permite prezentatorului să o execute
din nou. Acest lucru este util, de exemplu, atunci când un utilizator completează un formular și login-ul său expiră. Pentru
a nu pierde date, înainte de redirecționarea către pagina de autentificare, salvăm cererea curentă în sesiune folosind
$reqId = $this->storeRequest()
, care returnează un identificator sub forma unui șir scurt și îl transmite ca
parametru către prezentatorul de autentificare.
După conectare, apelăm metoda $this->restoreRequest($reqId)
, care preia cererea din sesiune și o transmite
acesteia. Metoda verifică dacă cererea a fost creată de același utilizator ca și cel care s-a logat acum este. Dacă un alt
utilizator se conectează sau dacă cheia nu este validă, nu face nimic și programul continuă.
Consultați cartea de bucate Cum să vă întoarceți la o pagină anterioară.
Canonizare
Prezentatorii au o caracteristică foarte bună care îmbunătățește SEO (optimizarea capacității de căutare pe
Internet). Acestea previn în mod automat existența conținutului duplicat la diferite URL-uri. Dacă mai multe URL-uri duc la
o anumită destinație, de exemplu /index
și /index?page=1
, cadrul desemnează unul dintre ele ca
fiind primar (canonic) și le redirecționează pe celelalte către acesta folosind codul HTTP 301. Datorită acestui lucru,
motoarele de căutare nu indexează paginile de două ori și nu le slăbește page rank-ul.
Acest proces se numește canonizare. URL-ul canonic este URL-ul generat de router, de obicei prima rută adecvată din colecție.
Canonizarea este activată în mod implicit și poate fi dezactivată prin intermediul
$this->autoCanonicalize = false
.
Redirecționarea nu are loc cu o cerere AJAX sau POST, deoarece ar duce la pierderea de date sau nu ar avea nicio valoare adăugată SEO.
De asemenea, puteți invoca manual canonizarea folosind metoda canonicalize()
, care, ca și metoda
link()
, primește ca argumente prezentatorul, acțiunile și parametrii. Aceasta creează o legătură și
o compară cu URL-ul curent. Dacă este diferit, redirecționează către link-ul generat.
public function actionShow(int $id, string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// redirecționează dacă $slug este diferit de $realSlug
$this->canonicalize('Product:show', [$id, $realSlug]);
}
Evenimente
Pe lângă metodele startup()
, beforeRender()
și shutdown()
, care sunt apelate ca parte
a ciclului de viață al prezentatorului, pot fi definite și alte funcții care să fie apelate automat. Prezentatorul definește
așa-numitele evenimente, iar dumneavoastră adăugați gestionarii
acestora la array-urile $onStartup
, $onRender
și $onShutdown
.
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct()
{
$this->onStartup[] = function () {
// ...
};
}
}
Manipulatorii din array-ul $onStartup
sunt apelați chiar înainte de metoda startup()
, apoi
$onRender
între beforeRender()
și render<View>()
și, în sfârșit,
$onShutdown
chiar înainte de shutdown()
.
Răspunsuri
Răspunsul returnat de către prezentator este un obiect care implementează interfața Nette\Application\Response. Există o serie de răspunsuri gata făcute:
- Nette\Application\Responses\CallbackResponse – trimite un callback
- Nette\Application\Responses\FileResponse – trimite fișierul
- Nette\Application\Responses\ForwardResponse – transmite ()
- Nette\Application\Responses\JsonResponse – trimite JSON
- Nette\Application\Responses\RedirectResponse – redirecționează
- Nette\Application\Responses\TextResponse – trimite text
- Nette\Application\Responses\VoidResponse – răspuns gol
Răspunsurile sunt trimise prin metoda sendResponse()
:
use Nette\Application\Responses;
// Text simplu
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));
// Trimite un fișier
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));
// Trimite un callback
$callback = function (Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) {
if ($httpResponse->getHeader('Content-Type') === 'text/html') {
echo '<h1>Hello</h1>';
}
};
$this->sendResponse(new Responses\CallbackResponse($callback));