Vytváření odkazů URL

Tvořit odkazy v Nette je jednoduché, jako ukazovat prstem. Stačí jen namířit a framework už za vás všechnu práci udělá. Ukážeme si:

  • jak vytvářet odkazy v šablonách i jinde
  • jak odlišit odkaz na aktuální stránku
  • co s neplatnými odkazy

Díky obousměrnému routování nebudete nikdy muset do šablon či kódu zapisovat navrdo URL adresy vaší aplikace, které se mohou později měnit, nebo je komplikovaně skládat. V odkazu stačí uvést presenter a akci, předat případné parametry a framework už URL vygeneruje sám. Vlastně je to velice podobné, jako když voláte funkci. To se vám bude líbit.

V šabloně presenteru

Nejčastěji vytváříme odkazy v šablonách a skvělým pomocníkem je atribut n:href:

<a n:href="Product:show">detail</a>

Všimněte si, že místo HTML atributu href jsme použili n:atribut n:href. Jeho hodnotou pak není URL, jak by tomu bylo v případě atributu href, ale název presenteru a akce.

Kliknutí na odkaz je, zjednodušeně řečeno, něco jako zavolání metody ProductPresenter::renderShow(). A pokud má ve své signatuře parametry, můžeme ji volat s argumenty:

<a n:href="Product:show $product->id, $product->slug">detail produktu</a>

Na rozdíl od PHP je možné předávat i pojmenované parametry. Následující odkaz předává parametr lang s hodnotou cs:

<a n:href="Product:show $product->id, lang => cs">detail produktu</a>

Pokud metoda ProductPresenter::renderShow() nemá $lang ve své signatuře, může si hodnotu parametru zjistit pomocí $lang = $this->getParameter('lang').

Pokud jsou parametry uložené v poli, lze je rozvinout operátorem (expand) (něco jako ... operátor v PHP, ale funguje s asociativními poli):

{var $args = [$product->id, lang => cs]}
<a n:href="Product:show (expand) $args">detail produktu</a>

Atribut n:href je velmi šikovný pro HTML značky <a>. Chceme-li odkaz vypsat jinde, například v textu, použijeme {link}:

Adresa je: {link Homepage:default}

V kódu

K vytvoření odkazu v presenteru slouží metoda link():

$url = $this->link('Product:show', $product->id);

Parametry lze předat také pomocí pole, kde lze uvést i pojmenované parametry:

$url = $this->link('Product:show', [$product->id, 'lang' => 'cs']);

Odkazy lze vytvářet i bez presenteru, od toho je tu LinkGenerator a jeho metoda link().

Odkazy na presenter

Pokud je cílem odkazu presenter a akce, má tuto syntaxi:

[//] [[[[:]module:]presenter:]action | this] [#fragment]

Formát podporují všechny značky Latte a všechny metody presenteru, které s odkazy pracují, tedy n:href, {link}, {plink}, link(), lazyLink(), isLinkCurrent(), redirect(), redirectPermanent(), forward(), canonicalize() a také LinkGenerator. Takže i když je v příkladech použit n:href, mohla by tam být kterákoliv z funkcí.

Základním tvarem je tedy Presenter:action:

<a n:href="Homepage:default">úvodní stránka</a>

Pokud odkazujeme na akci aktuálního presenteru, můžeme jeho název vynechat:

<a n:href="default">úvodní stránka</a>

Pokud je cílem akce default, můžeme ji vynechat, ale dvojtečka musí zůstat:

<a n:href="Homepage:">úvodní stránka</a>

Odkazy mohou také směřovat do jiných modulů. Zde se odkazy rozlišují na relativní do zanořeného submodulu, nebo absolutní. Princip je analogický k cestám na disku, jen místo lomítek jsou dvojtečky. Předpokládejme, že aktuální presenter je součástí modulu Front, potom zapíšeme:

<a n:href="Shop:Product:show">odkaz na Front:Shop:Product:show</a>
<a n:href=":Admin:Product:show">odkaz na Admin:Product:show</a>

Speciálním případem je odkaz na sebe sama, kdy jako cíl uvedeme this.

<a n:href="this">refresh</a>

Odkazovat můžeme na určitou část stránky přes tzv. fragment za znakem mřížky #:

<a n:href="Homepage:#main">odkaz na Homepage:default a fragment #main</a>

Vygenerovaný odkaz je ve tvaru relativní URL cesty. Pokud potřebujeme absolutní odkaz včetně domény, přidáme na začátek dvě lomítka (např. n:href="//Homepage:"). Nebo lze přepnout presenter, aby generoval jen absolutní odkazy.

Odkazy na signál

Cílem odkazu nemusí být jen presenter a akce, ale také signál (volají metodu handle<Signal>()). Pak je syntaxe následující:

[//] [sub-component:]signal! [#fragment]

Signál tedy odlišuje vykřičník:

<a n:href="click!">signal</a>

Lze vytvořit i odkaz na signál subkomponenty (nebo sub-subkomponenty):

<a n:href="componentName:click!">signal</a>

Odkazy v komponentě

Protože komponenty jsou samostatné znovupoužitelné celky, které by neměly mít žádné vazby na okolní presentery, fungují tu odkazy trošku jinak. Atribut Latte n:href a značka {link} i metody komponent jako je link() a další považují cíl odkazu vždy za název signálu. Proto není nutné ani uvádět vykřičník:

<a n:href="click">signál, nikoliv akce</a>

Pokud bychom chtěli v šabloně komponenty odkazovat na presentery, použijeme k tomu značku {plink}:

<a href="{plink Homepage:default}">úvod</a>

nebo v kódu

$this->getPresenter()->link('Homepage:default')

Odkazy na aktuální stránku

Metoda presenteru isLinkCurrent() zjišťuje, zda je cíl odkazu shodný s aktuální stránkou. Toho lze využít například v šabloně k odlišení odkazů apod.

Parametry jsou stejné jako u metody link(), navíc je však možné místo konkrétní akce uvést zástupný znak *, který znamená jakoukoliv akci daného presenteru.

{if !$presenter->isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">Přihlaste se</a>
{/if}

<li n:class="$presenter->isLinkCurrent('Product:*') ? active">
	<a n:href="Product:">...</a>
</li>

V kombinaci s n:href v jednom elementu se dá použít zkrácená podoba:

<a n:class="$presenter->isLinkCurrent() ? active" n:href="Homepage:">...</a>

Zástupný znak * lze použít pouze místo akce, nikoliv presenteru. Pro zjištění, zda jsme v určitém modulu nebo jeho submodulu, použijeme metodu $presenter->isModuleCurrent(moduleName).

<li n:class="$presenter->isModuleCurrent('Forum:Users') ? active">
	<a n:href="Product:">...</a>
</li>

Neplatné odkazy

Může se stát, že vytvoříme neplatný odkaz – buď proto, že vede na neexistující presenter, nebo proto, že předává víc parametrů, než které cílová metoda přijímá ve své signatuře, nebo když pro cílovou akci nelze vygenerovat URL. Jak naložit s neplatnými odkazy určuje statická proměnná Presenter::$invalidLinkMode. Ta může nabývat kombinaci těchto hodnot (konstant):

  • Presenter::INVALID_LINK_SILENT – tichý režim, jako URL se vrátí znak #
  • Presenter::INVALID_LINK_WARNING – vyhodí se varování E_USER_WARNING, které bude v produkčním režimu zalogováno, ale nezpůsobí přerušení běhu skriptu
  • Presenter::INVALID_LINK_TEXTUAL – vizuální varování, vypíše chybu přímo do odkazu
  • Presenter::INVALID_LINK_EXCEPTION – vyhodí se výjimka InvalidLinkException

Výchozí nastavení je INVALID_LINK_WARNING v produkčním režimu a INVALID_LINK_WARNING | INVALID_LINK_TEXTUAL ve vývojovém. INVALID_LINK_WARNING v produkčním prostředí nezpůsobí přerušení skriptu, ale varování bude zalogováno. Ve vývojovém prostředí ho zachytí Tracy a zobrazí bluescreen. INVALID_LINK_TEXTUAL pracuje tak, že jako URL vrátí chybovou zprávu, která začíná znaky #error:. Aby takové odkazy byly na první pohled patrné, doplníme si do CSS:

a[href^="#error:"] {
	background: red;
	color: white;
}

Pokud nechceme, aby se ve vývojovém prostředí produkovala varování, můžeme nastavit tichý režim přímo v konfiguraci.

application:
	silentLinks: true

LinkGenerator

Jak vytvářet odkazy s podobným komfortem jako má metoda link(), ale bez přítomnosti presenteru? Od toho je tu Nette\Application\LinkGenerator.

LinkGenerátor je služba, kterou si můžete nechat předat přes konstruktor a poté vytvářet odkazy jeho metodou link().

Oproti presenterům je tu rozdíl. LinkGenerator vytváří všechny odkazy rovnou jako absolutní URL. A dále neexistuje žádný „aktuální presenter“, takže nelze jako cíl uvést jen název akce link('default') nebo uvádět relativní cesty k modulům.

Neplatné odkazy vždy vyhazují Nette\Application\UI\InvalidLinkException.