Tworzenie odnośników URL
Tworzenie odnośników w Nette jest tak proste jak wskazanie palcem. Wystarczy wycelować, a framework wykona za Ciebie całą pracę. Pokażemy:
- jak tworzyć odnośniki w szablonach i gdzie indziej
- jak odróżnić odnośnik do bieżącej strony
- co zrobić z nieprawidłowymi odnośnikami
Dzięki dwukierunkowemu routingowi nigdy nie będziesz musiał zapisywać na sztywno w szablonach ani w kodzie URL swojej aplikacji, które mogą się później zmienić albo których składanie bywa skomplikowane. W odnośniku podajesz jedynie presenter i akcję, przekazujesz ewentualne parametry, a framework sam wygeneruje URL. Właściwie przypomina to bardzo wywołanie funkcji. Spodoba Ci się to.
W szablonie presentera
Najczęściej tworzymy odnośniki w szablonach i świetnym pomocnikiem jest atrybut n:href:
<a n:href="Product:show">szczegóły</a>
Zwróć uwagę, że zamiast atrybutu HTML href użyliśmy n:atrybutu n:href. Jego wartością nie jest URL, jak
byłoby w przypadku atrybutu href, lecz nazwa presentera i akcji.
Kliknięcie w odnośnik to, mówiąc prosto, coś w rodzaju wywołania metody ProductPresenter::renderShow().
A jeśli ma ona w sygnaturze parametry, możemy wywołać ją z argumentami:
<a n:href="Product:show $product->id, $product->slug">szczegóły produktu</a>
Można też przekazywać parametry nazwane. Poniższy odnośnik przekazuje parametr lang o wartości
en:
<a n:href="Product:show $product->id, lang: en">szczegóły produktu</a>
Jeśli metoda ProductPresenter::renderShow() nie ma w sygnaturze $lang, może pobrać wartość
parametru przez $lang = $this->getParameter('lang') albo z właściwości.
Jeśli parametry są w tablicy, można je rozwinąć operatorem ...:
{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">szczegóły produktu</a>
W odnośnikach automatycznie przekazywane są również tak zwane parametry trwałe.
Atrybut n:href jest bardzo poręczny dla tagów HTML <a>. Jeśli chcemy wypisać odnośnik
gdzie indziej, na przykład w tekście, użyjemy {link}:
URL to: {link Home:default}
W kodzie
Do utworzenia odnośnika w presenterze służy metoda link():
$url = $this->link('Product:show', $product->id);
Parametry można przekazać także jako tablicę, w której można podać również parametry nazwane:
$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);
Odnośniki można tworzyć również bez presentera, za pomocą LinkGenerator i jego metody
link().
Czasem potrzebujesz utworzyć odnośnik teraz, ale wygenerować rzeczywisty URL dopiero później. Służy do tego metoda
lazyLink(), która zwraca obiekt Nette\Application\UI\Link. Zaletą jest to, że możesz ten obiekt
przekazać dalej, na przykład do szablonu, i przed jego wyrenderowaniem nadal dostosowywać jego parametry metodą
setParameter(). Sam URL składany jest dopiero wtedy, gdy obiekt zostanie skonwertowany na string:
$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // URL generowany jest dopiero tutaj
Odnośniki do presentera
Jeśli celem odnośnika jest presenter i akcja, ma on taką składnię:
[//] [[[[:]moduł:]presenter:]akcja | this] [#fragment]
Format ten obsługują wszystkie tagi Latte i wszystkie metody presentera pracujące z odnośnikami, czyli
n:href, {link}, {plink}, link(), lazyLink(),
isLinkCurrent(), redirect(), redirectPermanent(), forward(),
canonicalize(), a także LinkGenerator. Choć więc w przykładach używane jest
n:href, mogłaby tam stać dowolna z tych funkcji.
Podstawową postacią jest więc Presenter:akcja:
<a n:href="Home:default">strona główna</a>
Jeśli linkujemy do akcji bieżącego presentera, możemy pominąć jego nazwę:
<a n:href="default">strona główna</a>
Jeśli akcją docelową jest default, możemy ją pominąć, ale dwukropek musi zostać:
<a n:href="Home:">strona główna</a>
Odnośniki mogą wskazywać także na inne moduły. Rozróżniamy tu odnośniki
względne wobec zagnieżdżonego submodułu albo bezwzględne. Zasada jest analogiczna do ścieżek dyskowych, tyle że zamiast
ukośników używa się dwukropków. Zakładając, że bieżący presenter jest częścią modułu Front,
zapisalibyśmy:
<a n:href="Shop:Product:show">odnośnik do Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">odnośnik do Admin:Product:show</a>
Szczególnym przypadkiem jest odnośnik do samego siebie, gdzie jako cel
podajemy this.
<a n:href="this">odśwież</a>
Do konkretnej części strony możemy linkować przez tak zwany fragment po znaku kratki #:
<a n:href="Home:#main">odnośnik do Home:default i fragmentu #main</a>
Fragment można też ustawić dynamicznie jako argument z kluczem #. Jego wartość jest
automatycznie kodowana i ma pierwszeństwo przed fragmentem podanym w celu:
$this->link('Home:default', ['#' => $fragment]);
Ścieżki bezwzględne
Odnośniki generowane przez link() albo n:href są zawsze ścieżkami bezwzględnymi (czyli
zaczynają się od /), ale nie bezwzględnymi URL z protokołem i domeną, jak https://domain.
Aby wygenerować bezwzględny URL, dodaj na początku dwa ukośniki (np. n:href="//Home:"). Alternatywnie możesz
przełączyć presenter tak, aby generował wyłącznie odnośniki bezwzględne, ustawiając
$this->absoluteUrls = true.
Do zamiany ścieżki względnej na bezwzględną można też użyć w szablonie filtra |absoluteUrl.
Odnośnik do bieżącej strony
Cel this tworzy odnośnik do bieżącej strony:
<a n:href="this">odśwież</a>
Jednocześnie przekazywane są wszystkie parametry podane w sygnaturze metody action<Akcja>() albo
render<Widok>() (jeśli action<Akcja>() nie jest zdefiniowana). Jeśli więc jesteśmy na
stronie Product:show z id: 123, odnośnik do this przekaże również ten parametr.
Oczywiście parametry można podać bezpośrednio:
<a n:href="this refresh: 1">odśwież</a>
Funkcja isLinkCurrent() sprawdza, czy cel odnośnika jest identyczny z bieżącą stroną. Można tego użyć na
przykład w szablonie do odróżniania odnośników itd.
Parametry są takie same jak dla metody link(), ale zamiast konkretnej akcji można użyć symbolu wieloznacznego
*, który oznacza dowolną akcję danego presentera.
{if !isLinkCurrent('Admin:login')}
<a n:href="Admin:login">Zaloguj się</a>
{/if}
<li n:class="isLinkCurrent('Product:*') ? active">
<a n:href="Product:">...</a>
</li>
W połączeniu z n:href w jednym elemencie można użyć postaci skróconej:
<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>
Symbolu wieloznacznego * można użyć tylko zamiast akcji, nie presentera.
Aby ustalić, czy jesteśmy w konkretnym module albo jego submodule, użyj metody isModuleCurrent(moduleName).
<li n:class="isModuleCurrent('Forum:Users') ? active">
<a n:href="Product:">...</a>
</li>
Zmiana bazy odnośników
Domyślnie odnośniki względne wyprowadzane są z bieżącego presentera. Można to zmienić za pomocą
{linkBase}:
{linkBase Admin:Dashboard}
<a n:href="Product:show">szczegóły produktu</a>
Odnośnik poprowadzi do Admin:Dashboard:Product:show. Dotyczy to wyłącznie odnośników względnych –
odnośniki bezwzględne zaczynające się dwukropkiem oraz odnośniki do bieżącego presentera (this,
show) pozostają bez zmian.
{linkBase} obowiązuje w całym szablonie i przydaje się zwłaszcza w szablonach layoutu, gdzie zapewnia spójne
odnośniki niezależnie od wywołującego presentera. Tag musi znaleźć się na początku szablonu, w przeciwnym razie zgłasza
CompileException.
Odnośniki do sygnału
Celem odnośnika nie musi być tylko presenter i akcja, ale też sygnał (wywołuje wtedy metodę
handle<Sygnał>()). Składnia wygląda wtedy tak:
[//] [subkomponent:]sygnał! [#fragment]
Sygnał odróżnia więc wykrzyknik:
<a n:href="click!">sygnał</a>
Możesz też utworzyć odnośnik do sygnału subkomponentu (albo sub-subkomponentu):
<a n:href="componentName:click!">sygnał</a>
Odnośniki w komponencie
Ponieważ komponenty są osobnymi jednostkami wielokrotnego
użytku, które nie powinny mieć żadnych powiązań z otaczającymi presenterami, odnośniki działają tu nieco inaczej.
Atrybut Latte n:href i tag {link}, a także metody komponentu, takie jak link() i inne,
zawsze traktują cel odnośnika jako nazwę sygnału. Nie trzeba więc nawet dodawać wykrzyknika:
<a n:href="click">sygnał, nie akcja</a>
Gdybyśmy chcieli w szablonie komponentu linkować do presenterów, użyjemy tagu {plink}:
<a href={plink Home:default}>strona główna</a>
albo w kodzie
$this->getPresenter()->link('Home:default')
Aliasy
Czasem przydatne bywa przypisanie parze Presenter:akcja łatwego do zapamiętania aliasu. Na przykład nazwanie strony
głównej Front:Home:default po prostu home, a Admin:Dashboard:default jako
admin.
Aliasy definiuje się w konfiguracji pod kluczem
application › aliases:
application:
aliases:
home: Front:Home:default
admin: Admin:Dashboard:default
sign: Front:Sign:in
W odnośnikach zapisuje się je potem za pomocą małpy, na przykład:
<a n:href="@admin">administracja</a>
Obsługiwane są też we wszystkich metodach pracujących z odnośnikami, takich jak redirect() i podobne.
Nieprawidłowe odnośniki
Może się zdarzyć, że utworzymy nieprawidłowy odnośnik: albo dlatego, że prowadzi do nieistniejącego presentera, albo
dlatego, że przekazuje więcej parametrów, niż przyjmuje w sygnaturze metoda docelowa, albo gdy dla docelowej akcji nie da się
wygenerować URL. Sposób obsługi nieprawidłowych odnośników ustawia się w presenterze przez
$this->invalidLinkMode. Może przyjmować kombinację tych wartości (stałych):
Presenter::InvalidLinkSilent– tryb cichy, jako URL zwraca znak #Presenter::InvalidLinkWarning– zgłaszane jest ostrzeżenie E_USER_WARNING, które w trybie produkcyjnym zostanie zalogowane, ale nie przerwie wykonywania skryptuPresenter::InvalidLinkTextual– ostrzeżenie wizualne, wypisuje błąd bezpośrednio w odnośnikuPresenter::InvalidLinkException– zgłasza InvalidLinkException
Ustawieniem domyślnym jest InvalidLinkWarning w trybie produkcyjnym i
InvalidLinkWarning | InvalidLinkTextual w trybie deweloperskim. InvalidLinkWarning w środowisku
produkcyjnym nie powoduje przerwania skryptu, ale ostrzeżenie zostanie zalogowane. W środowisku deweloperskim przechwytuje je Tracy i wyświetla bluescreen. InvalidLinkTextual działa tak, że jako URL
zwraca komunikat o błędzie zaczynający się od znaków #error:. Aby takie odnośniki rzucały się w oczy na
pierwszy rzut oka, dodaj do swojego CSS:
a[href^="#error:"] {
background: red;
color: white;
}
Jeśli nie chcemy, aby w środowisku deweloperskim powstawały ostrzeżenia, możemy wyciszyć je bezpośrednio w konfiguracji.
application:
silentLinks: true
LinkGenerator
Jak tworzyć odnośniki z podobną wygodą jak metodą link(), ale bez obecności presentera? Od tego jest Nette\Application\LinkGenerator.
LinkGenerator to usługa, którą możesz otrzymać przez konstruktor, a następnie tworzyć odnośniki jej metodą
link().
W porównaniu z presenterami jest różnica. LinkGenerator tworzy wszystkie odnośniki bezpośrednio jako bezwzględne URL.
Ponadto nie istnieje “bieżący presenter”, nie da się więc podać jako celu samej nazwy akcji link('default')
ani używać ścieżek względnych do modułów.
Nieprawidłowe odnośniki zawsze zgłaszają Nette\Application\UI\InvalidLinkException.