Presentery
Przyjrzymy się temu, jak w Nette pisze się presentery i szablony. Po lekturze będziesz rozumieć:
- jak działają presentery
- czym są parametry trwałe
- jak renderowane są szablony
Wiemy już, że presenter to klasa reprezentująca konkretną stronę aplikacji webowej, na przykład stronę główną, produkt w sklepie internetowym, formularz logowania, feed z mapą strony itd. Aplikacja może mieć od jednego do tysięcy presenterów. W innych frameworkach znane są też jako kontrolery.
Zwykle terminem presenter określamy potomka klasy Nette\Application\UI\Presenter, która nadaje się do generowania interfejsów webowych i której poświęcona będzie reszta tego rozdziału. W ogólnym sensie presenterem jest dowolny obiekt implementujący interfejs Nette\Application\IPresenter.
Cykl życia presentera
Zadaniem presentera jest obsłużenie żądania i zwrócenie odpowiedzi (którą może być strona HTML, obraz, przekierowanie itd.).
Na początku przekazywane jest mu więc żądanie. Nie jest to bezpośrednio żądanie HTTP, lecz obiekt Nette\Application\Request, w który żądanie HTTP zostało przekształcone z pomocą routera. Zwykle nie pracujemy z tym obiektem bezpośrednio, bo presenter sprytnie deleguje obsługę żądania do innych metod, którym teraz się przyjrzymy.
Diagram pokazuje listę metod wywoływanych kolejno od góry do dołu, jeśli istnieją. Żadna z nich nie jest obowiązkowa; możesz mieć całkowicie pusty presenter bez jednej metody i zbudować na nim prostą statyczną witrynę.
__construct()
Konstruktor nie należy ściśle do cyklu życia presentera, bo wywoływany jest w chwili tworzenia obiektu. Wspominamy o nim jednak ze względu na jego znaczenie. Konstruktor (wraz z metodą inject) służy do przekazywania zależności.
Presenter nie powinien zajmować się logiką biznesową aplikacji, zapisywać do bazy danych ani z niej czytać, wykonywać
obliczeń itd. To odpowiedzialność klas w warstwie, którą nazywamy modelem. Na przykład klasa ArticleRepository
może odpowiadać za wczytywanie i zapisywanie artykułów. Aby presenter mógł z niej korzystać, musi mieć ją przekazaną przez wstrzykiwanie zależności:
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articles,
) {
}
}
startup()
Natychmiast po otrzymaniu żądania wywoływana jest metoda startup(). Możesz jej użyć do zainicjowania
właściwości, sprawdzenia uprawnień użytkownika itd. Wymagane jest, aby metoda ta zawsze wywoływała swojego rodzica:
parent::startup().
action<Akcja>(args...)
Podobna do metody render<Widok>(). Podczas gdy render<Widok>() ma przygotować dane dla
konkretnego szablonu, który następnie zostanie wyrenderowany, action<Akcja>() przetwarza żądanie bez
konieczności renderowania potem szablonu. Może na przykład przetworzyć dane, zalogować albo wylogować użytkownika itd., a
następnie przekierować gdzie indziej.
Ważne jest, że action<Akcja>() wywoływana jest przed render<Widok>(). Pozwala
nam to ewentualnie zmienić przebieg żądania w metodzie akcji, na przykład zmieniając szablon, który zostanie wyrenderowany,
albo nawet metodę render<Widok>(), która zostanie wywołana, za pomocą
setView('otherView').
Możesz nawet przełączyć się na zupełnie inną akcję metodą switch('otherAction').
Przerywa ona bieżącą metodę i zamiast niej uruchamia metody action<Akcja>() i
render<Widok>() nowej akcji (oraz wyłącza automatyczną kanonizację). Samo
żądanie trwa dalej; przerywana jest tylko aktualnie działająca metoda.
Do metody przekazywane są parametry z żądania. Można i zalecamy podać dla nich typy, np.
actionShow(int $id, ?string $slug = null). Jeśli parametru id brakuje albo nie jest liczbą
całkowitą, presenter zwraca błąd 404 i kończy działanie.
handle<Sygnał>(args...)
Ta metoda przetwarza tak zwane sygnały, o których dowiemy się w rozdziale poświęconym komponentom. Przeznaczona jest przede wszystkim dla komponentów i obsługi żądań AJAX.
Do metody przekazywane są parametry z żądania, tak jak przy action<Akcja>(), wraz
z kontrolą typów.
beforeRender()
Metoda beforeRender, jak sama nazwa wskazuje, wywoływana jest przed każdą metodą
render<Widok>(). Służy do wspólnej konfiguracji szablonu, przekazywania zmiennych do layoutu
i podobnych zadań.
render<Widok>(args...)
Tutaj przygotowujemy szablon do późniejszego renderowania, przekazujemy mu dane itd.
Do metody przekazywane są parametry z żądania, tak jak przy action<Akcja>(), wraz
z kontrolą typów.
public function renderShow(int $id): void
{
// pobieramy dane z modelu i przekazujemy do szablonu
$this->template->article = $this->articles->getById($id);
}
afterRender()
Metoda afterRender, jak znów wskazuje nazwa, wywoływana jest po każdej metodzie
render<Widok>(). Używana jest raczej rzadko.
shutdown()
Wywoływana na końcu cyklu życia presentera.
Zdarzenia
Poza metodami startup(), beforeRender() i shutdown(), wywoływanymi w ramach cyklu
życia presentera, można zdefiniować inne funkcje, które będą wywoływane automatycznie. Presenter definiuje tak zwane zdarzenia, a ich handlery dodajesz do tablic $onStartup,
$onRender i $onShutdown.
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct()
{
$this->onStartup[] = function () {
// ...
};
}
}
Handlery z tablicy $onStartup wywoływane są tuż przed metodą startup(), handlery
$onRender między beforeRender() a render<Widok>(), a wreszcie handlery
$onShutdown tuż przed shutdown().
Rada, zanim ruszymy dalej: Jak widzisz, presenter może obsługiwać wiele akcji/widoków, czyli mieć wiele metod
render<Widok>(). Zalecamy jednak projektować presentery z jedną albo możliwie niewieloma akcjami.
Wysyłanie odpowiedzi
Odpowiedzią presentera jest zwykle wyrenderowanie szablonu do strony HTML, ale może to być również wysłanie pliku, JSON-a albo choćby przekierowanie na inną stronę.
W dowolnym momencie cyklu życia możemy użyć jednej z poniższych metod, aby wysłać odpowiedź i jednocześnie zakończyć działanie presentera:
redirect(),redirectPermanent(),redirectUrl()iforward()wykonują przekierowanieerror()kończy presenter z powodu błędusendJson($data)kończy presenter i wysyła dane w formacie JSONsendTemplate()kończy presenter i natychmiast renderuje szablonsendResponse($response)kończy presenter i wysyła własną odpowiedźterminate()kończy presenter bez odpowiedzi
Każda z tych metod natychmiast kończy presenter, zgłaszając cichy wyjątek zakończenia
Nette\Application\AbortException.
Jeśli nie wywołasz żadnej z tych metod, presenter automatycznie przechodzi do wyrenderowania szablonu. Dlaczego? Bo w 99% przypadków chcemy wyrenderować szablon, więc presenter przyjmuje to zachowanie jako domyślne, aby ułatwić nam pracę.
Tworzenie odnośników
Presenter ma metodę link(), która służy do tworzenia odnośników URL do innych presenterów. Pierwszym
parametrem jest docelowy presenter i akcja, po nim argumenty, które można przekazać jako tablicę:
$url = $this->link('Product:show', $id);
$url = $this->link('Product:show', [$id, 'lang' => 'en']);
W szablonie odnośniki do innych presenterów i akcji tworzy się tak:
<a n:href="Product:show $id">szczegóły produktu</a>
Zamiast prawdziwego URL po prostu zapisujesz znajomą parę Presenter:akcja i dodajesz ewentualne parametry.
Sztuczka tkwi w n:href, które mówi Latte, aby ten atrybut przetworzyło i wygenerowało prawdziwy URL. W Nette w
ogóle nie musisz myśleć o URL, tylko o presenterach i akcjach.
Więcej informacji znajdziesz w rozdziale Tworzenie odnośników URL.
Przekierowanie
Do przejścia na inny presenter służą metody redirect() i forward(). Mają bardzo podobną
składnię do metody link().
Metoda forward() przechodzi na nowy presenter natychmiast, bez przekierowania HTTP:
$this->forward('Product:show');
Przykład tymczasowego przekierowania z kodem HTTP 302 (albo 303, jeśli metodą bieżącego żądania jest POST):
$this->redirect('Product:show', $id);
Aby uzyskać trwałe przekierowanie z kodem HTTP 301, użyj tego:
$this->redirectPermanent('Product:show', $id);
Na inny URL poza aplikacją możesz przekierować metodą redirectUrl(). Kod HTTP można podać jako drugi
parametr; domyślnie jest to 302 (albo 303, jeśli metodą bieżącego żądania jest POST):
$this->redirectUrl('https://nette.org');
Przekierowanie natychmiast kończy działanie presentera, zgłaszając tak zwany cichy wyjątek zakończenia
Nette\Application\AbortException.
Przed przekierowaniem można wysłać wiadomości flash, czyli wiadomości, które wyświetlą się w szablonie po przekierowaniu.
Wiadomości flash
To wiadomości informujące zwykle o wyniku jakiejś operacji. Ważną cechą wiadomości flash jest to, że pozostają dostępne w szablonie również po przekierowaniu. Po wyświetleniu pozostają aktywne przez kolejne 30 sekund, na przykład gdyby użytkownik odświeżył stronę z powodu błędu transmisji – wiadomość nie zniknie od razu.
Wystarczy wywołać metodę flashMessage(), a
presenter zajmie się przekazaniem jej do szablonu. Pierwszym parametrem jest tekst wiadomości, a opcjonalnym drugim jej typ (np.
error, warning, info). Metoda flashMessage() zwraca instancję wiadomości flash, do której można dodać dalsze
informacje.
$this->flashMessage('Element został usunięty.');
$this->redirect(/* ... */); // i przekierowujemy
W szablonie wiadomości te dostępne są w zmiennej $flashes jako obiekty stdClass zawierające
właściwości message (tekst wiadomości), type (typ wiadomości) i ewentualnie wspomniane wcześniej
informacje dodane przez użytkownika. Renderujemy je tak:
{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
Błąd 404 itd.
Jeśli żądania nie da się spełnić, na przykład dlatego, że artykuł, który chcemy wyświetlić, nie istnieje w bazie
danych, zgłaszamy błąd 404 metodą error(string $message = '', int $httpCode = 404).
public function renderShow(int $id): void
{
$article = $this->articles->getById($id);
if (!$article) {
$this->error();
}
// ...
}
Kod błędu HTTP można przekazać jako drugi parametr; domyślnie jest to 404. Metoda działa tak, że zgłasza
Nette\Application\BadRequestException, po czym Application przekazuje sterowanie do error-presentera. To
presenter, którego zadaniem jest wyświetlenie strony informującej o zaistniałym błędzie. Error-presenter konfiguruje się w
konfiguracji aplikacji.
Wysyłanie JSON
Metoda sendJson($data) koduje podane dane do JSON, wysyła je jako odpowiedź HTTP i kończy presenter.
Przykład:
public function actionData(): void
{
$data = ['hello' => 'nette'];
$this->sendJson($data);
}
Parametry żądania
Presenter, a także każdy komponent, pozyskuje swoje parametry z żądania HTTP. Ich wartości możesz pobrać metodami
getParameter($name) albo getParameters(). Wartościami są stringi albo tablice stringów, czyli w
istocie surowe dane pozyskane bezpośrednio z URL.
Dla większej wygody zalecamy sięganie po parametry przez właściwości. Wystarczy oznaczyć je atrybutem
#[Parameter]:
use Nette\Application\Attributes\Parameter; // ten wiersz jest ważny
class HomePresenter extends Nette\Application\UI\Presenter
{
#[Parameter]
public string $theme; // musi być public
}
Dla właściwości zalecamy podanie typu danych (np. string), a Nette automatycznie odpowiednio rzutuje wartość.
Wartości parametrów można też walidować.
Przy tworzeniu odnośnika możesz ustawić wartość parametru bezpośrednio:
<a n:href="Home:default theme: dark">kliknij</a>
Parametry trwałe
Parametry trwałe służą do utrzymywania stanu między różnymi żądaniami. Ich wartość pozostaje taka sama również po
kliknięciu w odnośnik. W odróżnieniu od danych w sesji przesyłane są w URL. I dzieje się to całkowicie automatycznie, nie
ma więc potrzeby podawania ich jawnie w link() czy n:href.
Przykładowe zastosowanie? Wyobraź sobie, że masz aplikację wielojęzyczną. Bieżący język to parametr, który musi
zawsze być częścią URL. Ale dodawanie go do każdego odnośnika byłoby niesamowicie żmudne. Czynisz więc z niego parametr
trwały lang i będzie przenoszony automatycznie. Zgrabne!
Utworzenie parametru trwałego w Nette jest wyjątkowo proste. Wystarczy utworzyć właściwość publiczną i oznaczyć ją
atrybutem: (wcześniej używano /** @persistent */)
use Nette\Application\Attributes\Persistent; // ten wiersz jest ważny
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang; // musi być public
}
Jeśli $this->lang ma wartość w rodzaju 'en', to odnośniki tworzone przez link()
albo n:href będą zawierać również parametr lang=en. A po kliknięciu w odnośnik
$this->lang znów będzie 'en'.
Dla właściwości zalecamy podanie typu danych (np. string), możesz też podać wartość domyślną. Wartości
parametrów można walidować.
Parametry trwałe przenoszone są zwykle między wszystkimi akcjami danego presentera. Aby przenosić je również między wieloma presenterami, trzeba zdefiniować je albo:
- we wspólnym przodku, po którym presentery dziedziczą
- albo w traicie, którego presentery używają:
trait LanguageAware
{
#[Persistent]
public string $lang;
}
class ProductPresenter extends Nette\Application\UI\Presenter
{
use LanguageAware;
}
Przy tworzeniu odnośnika wartość parametru trwałego można zmienić:
<a n:href="Product:show $id, lang: cs">szczegóły po czesku</a>
Albo zresetować, czyli usunąć z URL. Przyjmie wtedy swoją wartość domyślną:
<a n:href="Product:show $id, lang: null">kliknij</a>
Wspólna przestrzeń parametrów
Parametry żądania, parametry trwałe oraz parametry metod action,
render i handle (sygnału) dzielą jedną przestrzeń, w której każdy identyfikowany jest swoją
nazwą. Jeśli ta sama nazwa pojawi się w kilku z nich, odnoszą się do jednej i tej samej wartości.
Często wykorzystuje się to z korzyścią. Na przykład parametr trwały lang i argument $lang
metody akcji albo sygnału to jedno i to samo – bieżącą wartość parametru trwałego odczytasz, po prostu wymieniając go
w sygnaturze metody:
#[Persistent]
public string $lang;
public function handleSearch(string $query, string $lang): void
{
// $lang zawiera bieżącą wartość parametru trwałego lang
}
Ponieważ przestrzeń ta jest wspólna, dbaj o unikalność nazw parametrów, chyba że celowo chcesz, aby dzieliły wartość. Dotyczy to również sygnałów, które dodatkowo odczytują parametry z ciała POST żądania, zobacz Sygnały w głąb.
Komponenty interaktywne
Presentery mają wbudowany system komponentów. Komponenty to osobne jednostki wielokrotnego użytku, które osadzamy w presenterach. Mogą to być formularze, datagridy, menu, w zasadzie wszystko, co warto wykorzystywać wielokrotnie.
Jak komponenty trafiają do presenterów i jak się ich potem używa? Dowiesz się tego w rozdziale Komponenty. Dowiesz się nawet, co mają wspólnego z Hollywood.
A skąd wziąć komponenty? Na Componette znajdziesz komponenty open source i wiele innych dodatków do Nette, przekazanych przez ochotników ze społeczności frameworka.
Idziemy głębiej
To, co omówiliśmy dotąd w tym rozdziale, wystarczy zapewne do większości zastosowań. Kolejne sekcje przeznaczone są dla tych, których interesuje głębsze zanurzenie w presentery i którzy chcą wiedzieć absolutnie wszystko.
Walidacja parametrów
Wartości parametrów żądania i parametrów
trwałych otrzymane z URL zapisywane są do właściwości metodą loadState(). Sprawdza ona również, czy typ
danych podany we właściwości się zgadza, w przeciwnym razie odpowie błędem 404 i strona nie zostanie wyświetlona.
Nigdy nie ufaj ślepo parametrom otrzymanym z URL, bo użytkownik może je łatwo nadpisać. Tak na przykład sprawdzimy, czy
język $this->lang należy do obsługiwanych. Odpowiednim sposobem jest nadpisanie wspomnianej metody
loadState():
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang;
public function loadState(array $params): void
{
parent::loadState($params); // tutaj ustawiane jest $this->lang
// następuje własne sprawdzenie wartości:
if (!in_array($this->lang, ['en', 'cs'])) {
$this->error();
}
}
}
Zapis i przywrócenie żądania
Żądanie obsługiwane przez presenter to obiekt Nette\Application\Request, zwracany metodą
presentera getRequest().
Bieżące żądanie można zapisać do sesji albo, odwrotnie, przywrócić je z niej i kazać presenterowi wykonać je
ponownie. Przydaje się to na przykład wtedy, gdy użytkownik wypełnia formularz, a jego sesja logowania wygasa. Aby nie
utracić danych, przed przekierowaniem na stronę logowania zapisujemy bieżące żądanie do sesji przez
$reqId = $this->storeRequest(). Zwraca to jego identyfikator w postaci krótkiego stringa, który następnie
przekazujemy jako parametr do presentera logowania.
Po zalogowaniu wywołujemy metodę $this->restoreRequest($reqId), która pobiera żądanie z sesji. Żądania
POST są do niego przekazywane, a pozostałe (GET) przekierowywane na URL żądania. Metoda sprawdza, czy żądanie zostało
utworzone przez tego samego użytkownika, który jest teraz zalogowany. Jeśli zaloguje się inny użytkownik albo klucz jest
nieprawidłowy, nie robi nic, a program działa dalej jak zwykle.
Zobacz przewodnik Jak wrócić do poprzedniej strony.
Kanonizacja
Presentery mają naprawdę znakomitą funkcję, która przyczynia się do lepszego SEO (Search Engine Optimization).
Automatycznie zapobiegają istnieniu zduplikowanej treści pod różnymi URL. Jeśli do konkretnego celu prowadzi kilka URL, np.
/index i /index?page=1, framework wyznacza jeden z nich jako podstawowy (kanoniczny) i przekierowuje
na niego pozostałe kodem HTTP 301. Dzięki temu wyszukiwarki nie indeksują Twoich stron dwa razy i nie rozmywają ich
page ranku.
Proces ten nazywa się kanonizacją. Kanonicznym URL jest ten wygenerowany przez router, zwykle pierwsza pasująca trasa w kolekcji.
Kanonizacja jest domyślnie włączona i można ją wyłączyć przez $this->autoCanonicalize = false.
Przekierowanie nie następuje przy żądaniach AJAX ani POST, bo mogłoby to prowadzić do utraty danych albo nie dawałoby żadnej dodatkowej wartości dla SEO.
Kanonizację możesz też wywołać ręcznie metodą canonicalize(). Podobnie jak metodzie link(),
przekazujesz jej presenter, akcję i parametry. Generuje odnośnik i porównuje go z bieżącym adresem URL. Jeśli się
różnią, przekierowuje na wygenerowany odnośnik.
public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// przekierowuje, jeśli $slug różni się od $realSlug
$this->canonicalize('Product:show', [$id, $realSlug]);
}
Kompletny wzorzec łączący filtry tras z canonicalize() w celu tworzenia przyjaznych SEO adresów URL znajdziesz
w Ładne URL ze slugami.
Odpowiedzi
Odpowiedź zwracana przez presenter to obiekt implementujący interfejs Nette\Application\Response. Dostępnych jest kilka gotowych odpowiedzi:
- Nette\Application\Responses\CallbackResponse – wysyła callback
- Nette\Application\Responses\FileResponse – wysyła plik
- Nette\Application\Responses\ForwardResponse – forward()
- Nette\Application\Responses\JsonResponse – wysyła JSON
- Nette\Application\Responses\RedirectResponse – przekierowanie
- Nette\Application\Responses\TextResponse – wysyła tekst
- Nette\Application\Responses\VoidResponse – pusta odpowiedź
Odpowiedzi wysyła się metodą sendResponse():
use Nette\Application\Responses;
// zwykły tekst
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));
// wysyła plik
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));
// wysyła callback
$callback = function (Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) {
if ($httpResponse->getHeader('Content-Type') === 'text/html') {
echo '<h1>Cześć</h1>';
}
};
$this->sendResponse(new Responses\CallbackResponse($callback));
Możesz też napisać własną odpowiedź. Wystarczy zaimplementować interfejs Nette\Application\Response, który
ma jedną metodę send() otrzymującą żądanie i odpowiedź HTTP. Przydaje się to na przykład przy
strumieniowaniu danych, których nie chcesz trzymać w pamięci:
class CsvResponse implements Nette\Application\Response
{
public function __construct(
private string $fileName,
private iterable $rows,
) {
}
public function send(Nette\Http\IRequest $request, Nette\Http\IResponse $response): void
{
$response->setContentType('text/csv', 'utf-8');
$response->sendAsFile($this->fileName);
$handle = fopen('php://output', 'w');
foreach ($this->rows as $row) {
fputcsv($handle, $row);
}
fclose($handle);
}
}
Wysyłasz ją potem w presenterze jak zwykle: $this->sendResponse(new CsvResponse('export.csv', $rows));
Cache HTTP
Metoda lastModified() ułatwia korzystanie z cache HTTP. Przekazujesz jej datę i czas ostatniej modyfikacji
treści (jako timestamp, string albo obiekt DateTimeInterface), a opcjonalnie walidator ETag (krótki string
identyfikujący bieżącą wersję treści, na przykład jej hash) i czas wygaśnięcia. Jeśli przeglądarka ma już pasującą
wersję, presenter wysyła odpowiedź 304 Not Modified i kończy działanie, dzięki czemu strona nie jest
niepotrzebnie renderowana ani przesyłana:
public function renderArticle(int $id): void
{
$article = $this->articles->getById($id);
$this->lastModified($article->updatedAt);
// ...
}
Finalizacja szablonu
Gdy presenter renderuje szablon, metoda sendTemplate() tuż przed renderowaniem wywołuje
completeTemplate(). Metoda ta uzupełnia zmienne oznaczone atrybutem #[TemplateVariable] i odnajduje
plik szablonu (zmienne domyślne ustawia już TemplateFactory przy tworzeniu szablonu). Możesz nadpisać tę metodę
chronioną, aby dodać zmienne wspólne dla wszystkich widoków albo ustawić inny plik:
protected function completeTemplate(Nette\Application\UI\Template $template): void
{
parent::completeTemplate($template);
$template->siteName = 'Moja aplikacja';
}
Ograniczanie dostępu przez #[Requires]
Atrybut #[Requires] daje zaawansowane możliwości ograniczania dostępu do presenterów i ich metod. Można nim
określić metody HTTP, wymagać żądania AJAX, ograniczyć do tego samego pochodzenia i dopuścić dostęp tylko przez forward.
Atrybut można zastosować zarówno do klas presenterów, jak i do poszczególnych metod, takich jak
action<Akcja>(), render<Widok>(), handle<Sygnał>() i
createComponent<Nazwa>().
Możesz określić te ograniczenia:
- na metody HTTP:
#[Requires(methods: ['GET', 'POST'])] - wymaganie żądania AJAX:
#[Requires(ajax: true)] - dostęp tylko z tego samego pochodzenia:
#[Requires(sameOrigin: true)] - dostęp tylko przez forward:
#[Requires(forward: true)] - ograniczenia na konkretne akcje:
#[Requires(actions: 'default')]
Od wersji 3.3 zgodność pochodzenia weryfikowana jest nagłówkiem przeglądarki Sec-Fetch-Site
(wcześniej przez ciasteczko SameSite), co jest bardziej niezawodne i sprawdza dokładną zgodność schematu, domeny
i portu.
Szczegóły znajdziesz w przewodniku Jak używać atrybutu Requires.
Kontrola metody HTTP
Presentery w Nette automatycznie weryfikują metodę HTTP każdego przychodzącego żądania, przede wszystkim ze względów
bezpieczeństwa. Domyślnie dozwolone są metody GET, POST, HEAD, PUT,
DELETE, PATCH.
Jeśli chcesz dodatkowo dopuścić na przykład metodę OPTIONS, użyj atrybutu #[Requires] (od Nette
Application v3.2.3):
#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
class MyPresenter extends Nette\Application\UI\Presenter
{
}
Od wersji 3.1.13 weryfikacja wykonywana jest w checkHttpMethod(), która sprawdza, czy metoda podana w żądaniu
znajduje się w tablicy $presenter->allowedMethods. Od wersji 3.2.3 podejście to jest przestarzałe na rzecz
#[Requires]. Metodę możesz nadpisać tak:
class MyPresenter extends Nette\Application\UI\Presenter
{
protected function checkHttpMethod(): void
{
$this->allowedMethods[] = 'OPTIONS';
parent::checkHttpMethod();
}
}
Warto podkreślić, że jeśli włączysz metodę OPTIONS, musisz następnie odpowiednio ją obsłużyć w swoim
presenterze. Metoda ta bywa używana jako tak zwane żądanie preflight, które przeglądarka wysyła automatycznie przed
właściwym żądaniem, gdy trzeba ustalić, czy żądanie jest dopuszczalne według polityki CORS (Cross-Origin Resource
Sharing). Jeśli włączysz metodę, ale nie zaimplementujesz poprawnej odpowiedzi, może to prowadzić do niespójności
i potencjalnych problemów z bezpieczeństwem.
Oznaczanie przestarzałych akcji
Atrybut #[Deprecated] oznacza akcje, sygnały albo całe presentery jako przestarzałe i przeznaczone do
usunięcia w przyszłości. Przy generowaniu odnośników do przestarzałych części aplikacji Nette zgłasza ostrzeżenie, aby
zwrócić uwagę programistów.
Atrybut możesz zastosować albo do całej klasy presentera, albo do poszczególnych metod action<Akcja>(),
render<Widok>() i handle<Sygnał>().