Routování v Nette - prakticky

Tento článek byl naposledy revidován, aktualizován a rozšířen 27. června 2014

V následujícím článku se budu opírat o teorii napsanou v dokumentaci. Jelikož jsem se však Nette učil sám, tak vím jak je těžké routování pochopit a zvlášť potom z dokumentace, která spíše ukazuje fičury, než jak na to. A vzhledem k tomu, že mi pod rukama prošlo velké množství velmi různorodých aplikací, kád bych zde uvedl příklady adresářové struktury, rout pro daný praktický problém a vzniklé URL adresy. Začíná přehlídka několika možných rout. Myslím si, že celá řada příkladů bude užitečnější, než teorie.

Základ všeho je porozumět tomu, jak se v Nette vytváří odkazy. Od toho se velmi podobně sestavují obecné routy tak, aby alespoň jedna seděla svým tvarem na daný odkaz.

První kroky

Začněme jednoduchou statickou stránkou, která má tuto jednoduchou adresářovou strukturu:

app/
├─── config/
├─── model/
├─── presenters/
│     └── HomepagePresenter.php
│
├─── router/
├─── templates/
│     ├── Homepage/
│     │    ├── kontakt.latte   (zde jednotlivé stránky statického webu)
│     │    └── ...
│     └── @layout.latte
│
└─── bootstrap.php

To znamená, že ne každou stránku se budu odkazovat přibližně jako Homepage:kontakt. Samozřejmě vždy se záměnou šablony (v tomto případě kontakt – kontakt.latte). To je dost triviální a stačilo by například:

$router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');

To je sice funkční, bohužel je to spíše teoretická routa, protože výsledek je otřesný:

http://zlml.cz/homepage/kontakt

Tuto routu píšu téměř všude. Je to routa velmi obecná a říká přibližně následující: Bude-li se někdo odkazovat v obecném tvaru Presenter:view, pochop Presenter jako název presenteru (např. HomepagePresen­ter) a hledej tedy soubor HomepagePresenter.php a view bude šablona presenteru, hledej ji tedy ve složce Presenter/view.latte a sestav URL která bude přesně v tomto tvaru. Homepage:default pouze říká co je výchozí hodnota a co se má hledat, pokud nebude specifikována konkrétní šablona.

Pro takto malý web je mnohem lepší specifikovat konkrétnější routu, která přijde před onu obecnou:

$router[] = new Route('<action>', 'Homepage:default');

Což udělá téměř to samé, jen vypustí z URL nadbytečnou informaci o presenteru. Vždy používáme HomepagePresenter, jen se mění cílová šablona podle URL:

http://zlml.cz/kontakt

Výsledná sada rout pro takovouto malou statickou stránku by tedy mohla vypadat takto:

public function createRouter() {
    $router = new RouteList();
    $router[] = new Route('<action>', 'Homepage:default');
    $router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default');
    return $router;
}

Dále je vhodné používat např. soubor sitemap.xml. Pokud ho také umístím do stejného adresáře jako šablony, routa je opět jednoduchá:

$router[] = new Route('sitemap.xml', 'Homepage:sitemap');

Hledá se šablona sitemap.latte. Přečtěte si jak vytvořit tuto šablonu v článku RSS a Sitemap jednoduše a rychle. Výsledná URL je tak jak má být:

http://zlml.cz/sitemap.xml

To samé lze udělal pro RSS.

Jdeme do hloubky

Trošku složitější routování přichází vždy když chcete udělat něco speciálního. Například to, aby číslo za URL udávalo číslo stránky v paginatoru:

http://zlml.cz/2
$router[] = new Route("[<paginator-page [1|2]>]", array(
    'presenter' => 'Homepage',
    'action' => 'default',
    'paginator-page' => 1
));

Zde už je nutné druhý parametr rozepsat a více specifikovat. Toto akceptuje pouze konkrétní čísla. a jako druhou specialitu lze napsat takovou routu, která bude tvořit URL z názvů článků:

http://zlml.cz/using-fulltext-searching-with-innodb
$router[] = new Route('<id>', array(
    'presenter' => 'Single',
    'action' => 'article',
    'id' => array(
        Route::FILTER_IN => function ($url) {
            return $this->posts->getIdByUrl($url);
        },
        Route::FILTER_OUT => function ($id) {
            return $this->posts->getUrlById($id);
        },
    ),
));

A není úplně na škodu vytvořit routu, která bude řešit napríklad vyhledávání:

http://zlml.cz/search/fio%20api
$router[] = new Route('search[/<search>]', 'Search:default');

Toto jsou jednoduché routy pro jednoduchou adresářovou strukturu. Lehce složitější jsou pro modulární strukturu, kdy je zapotřebí specifikovat modul:

http://zlml.cz/rss.xml
$router[] = new Route('rss.xml', 'Front:Blog:rss');

Chová se to stejně jako u předchozího příkladu se sitemap.xml, v tomto příkladu však routa hledá BlogPresenter.php ve složce FrontModule a šablonu rss.latte, také v tomto modulu. U rout pro modulární aplikace již raději rozepisuji druhý parametr, protože je to přehlednější. Následující routa zvládne jazykové mutace pro FrontModul, jinak je to opět ta nejobecnější routa vůbec:

http://zlml.cz/en/site/kontakt
$router[] = new Route('[<lang cs|sk|en>/]<presenter>/<action>[/<id>]', array(
    'module' => 'Front',
    'presenter' => 'Homepage',
    'action' => 'default',
));

To samé, ale opět o trošku náročnější. Tentokrát pro UserModule, který je na jiné URL, než FrontModule:

http://zlml.cz/user/en/setting/password
$router[] = new Route('user/[<lang cs|sk|en>/]<presenter>/<action>[/<id [0-9]+>]', array(
    'module' => 'User',
    'presenter' => 'Board',
    'action' => 'default',
));

A na závěr ještě poslední přehled možných rout jako příklady toho co je možné.

$router[] = new Route('sitemap.xml', 'Front:Export:sitemap');
$router[] = new Route('kategorie/<category>', 'Front:Product:default');
$router[] = new Route('produkt/<product>', 'Front:Product:detail');
$router[] = new Route('', 'Front:Product:default');
$router[] = new Route('admin/sign-<action>', 'Admin:Sign:');
$router[] = new Route('registrace/', 'Front:Register:new');
$router[] = new Route('index.php', 'Homepage:default', Route::ONE_WAY);
$router[] = new CliRouter(array('action' => 'Cli:Cli:cron'));
$router[] = new \App\RestRouter('api[/<presenter>[/<id>]]', array( //vyžaduje speciální objekt (není součástí Nette)
    'module' => 'Rest',
    'presenter' => 'Resource',
    'action' => 'get',
), \App\RestRouter::RESTFUL);

Je zcela zřejmé, že se všechny konstrukce stále opakují, proto považuji za opravdu důležité perfektně pochopit tvorbu odkazů a následně je to možná trochu o experimentování, ale s touto sadou příkladů bude myslím jednoduché najít podobnou routu, jaká je zrovna potřeba.

Jak na v posledním příkladu zmíněný CLI router se dočtete v článku Nette 2.1-dev CliRouter.

Máte nějakou zajímavou routu? Podělte se o ni… (-:

  • V komentářích jsou povolené HTML tagy <a href=""> <blockquote> <code> <em> <strong>
  • Kódy programů zapisujte takto pomocí <pre><code>alert('XSS');</code></pre>