Příklady routeru
- Petr Daňa
- Člen | 109
Taky bych potřeboval :) Konkrétně by mě zajímaly ty validation-expr u parametrů v routeru. Jsou to nebo nejsou klasické regexpy? Snažím se vytvořit router, který odchytí „nazevstranky.html“ a „nazevstranky.htm“ (jedná se o rozlišení těch přípon oproti jiným url), předá tento název (včetně přípony) jednomu presenteru a url bez .htm či .html předal jinému presenteru, tj. něco jako:
$router[] = new Route('<page [html?$]>', array('presenter' => 'Page', 'action' => 'default'));
$router[] = new Route('<presenter>/<action>', array('presenter' => 'Default', 'action' => 'default'));
Editoval Petr Daňa (22. 4. 2008 10:45)
- David Grudl
- Nette Core | 8218
Validační výrazy jsou klasické regulární výrazy. Vlastně se dá
říct, že pokud žádný validátor neuvedete, tak se použije
[^/]+
.
- David Grudl
- Nette Core | 8218
Petr Daňa napsal(a):
Snažím se vytvořit router, který odchytí „nazevstranky.html“ a „nazevstranky.htm“ (jedná se o rozlišení těch přípon oproti jiným url), předá tento název (včetně přípony) jednomu presenteru a url bez .htm či .html předal jinému presenteru, tj. něco jako:
Teoreticky to můžeš spojit do jedné routy:
$router[] = new Route(
'<page><ext (\.html|\.htm)>',
array(
'presenter' => 'Page',
'action' => 'default',
'ext' => NULL, // nyní je paramete <ext> nepovinný
)
);
- Petr Daňa
- Člen | 109
Díky za ukázku. Nakonec jsem vytvořil pro můj požadavek ty routery takhle a funguje to dobře.
$router[] = new Route('<page [^/]*(\.html|\.htm)>', array('presenter' => 'Page', 'action' => 'default'));
$router[] = new Route('<presenter>/<action>', array('presenter' => 'Default', 'action' => 'default'));
Akorát jsem se divil, proč když dám $ na konec (tj. <page [^/]*(\.html|\.htm)$> tak to nefunguje, pak jsem to prohnal debuggerem a koukal jsem, že tam na konec přidáváš lomítko, tak se to asi tlouklo.
- David Grudl
- Nette Core | 8218
Petr Daňa napsal(a):
Akorát jsem se divil, proč když dám $ na konec (tj. <page [^/]*(\.html|\.htm)$> tak to nefunguje, pak jsem to prohnal debuggerem a koukal jsem, že tam na konec přidáváš lomítko, tak se to asi tlouklo.
Je to tak, znak $
není potřeba (ani možné) přidávat.
- veena
- Člen | 98
Když se podíváme na ukázku nastavení routeru z Quickstartu:
$router = $application->getRouter();
$router[] = new Route('index.php', array(
'presenter' => 'Default',
'action' => 'default',
), Route::ONE_WAY);
$router[] = new Route('<presenter>/<action>/<id>', array(
'presenter' => 'Default',
'action' => 'default',
'id' => NULL,
));
Kdyby šlo při získání routeru mu hned předat routy, pak by mohlo vypadat nastavení takto úsporněji:
$router = $application->getRouter(
array('index.php', array('presenter' => 'Default', 'action' => 'default'), Route::ONE_WAY);
array('<presenter>/<action>/<id>', array('presenter' => 'Default', 'action' => 'default', 'id' => NULL)
... další seznam rout ...
);
Přemýšlím, jak to skloubit s moduly a připravovanými namespaces. Fajn by bylo mít moduly v oddělených adresářích, tam příslušné presentery.
Taky by šlo přidat pojmenované routy. Možná bych pro explicitní srozumitelnost přidal do rout i ^ a /$. Pak by nastavení routy i s ukázkou nepovinných parametrů (jméno routy, one way route flag) mohlo vypadat třeba takto:
array('^archiv/(\d{4})/(\d{2})/(\d{2})/([\w-]+)/$', 'blog:archive:article', 'name' => 'clanek v archivu', 'one way' => True)
Kde (\d{4})
je rok v url
(\d{2})
měsíc a den
([\w-]+)
nazev clanku
Závorky by způsobily, že se předají ty parametry view (render) metodě.
blog:archive:article
pak cesta typu
modul:presenter:view
tedy
vlastně adresář:třída:metoda
clanek v archivu
je jméno routy a dala by se z něj reversním
routingem a předáním parametrů zase vytvořit routa.
View metoda renderArticle()
by pak měla definici s parametry a
jejich případnými defaultními hodnotami normálně jak jsme zvyklí:
function renderArticle($year, $month, $day, $title) {}
Nicméně je to asi spíš návrh na implementaci just another router.
Jinak mi přijde trochu skryté, podle čeho se při generování
$presenter->link('hello', 123)
pozná, která routa se má
použít.
- David Grudl
- Nette Core | 8218
Asi tak… úspornost za každou cenu není cílem :-) Spíš srozumitelnost.
Vašku, mám pocit, že co tady popisuješ, jsou spíš Rails nebo něco podobného. Nette funguje jinak. Oba systémy pracují s URL a oba routují, ale jde o zcela jinou filosofii a jsem přesvědčen, že Nette ji má vyspělejší (kdybych o tom přesvědčen nebyl, implementuji routování, které se používá jinde).
Výhoda Nette je v tom, že nemusí pojmenovávat routy. Že má minimální závislosti mezi vrstavama (např. není vztah mezi pořadím parametrů v routě a argumenty metody presenteru) atd.
Naprosto chápu, že je těžké přijmout odlišnou filosofii, pokud je člověk zvyklý na něco jiného.
ad jmenné prostory a moduly: přesně tahle je to připravené, moduly jsou
vlastně jmenné prostory, ve kterých jsou presentery. Syntax
Modul:Submodul:Presenter:view
je už nyní platná.
- veena
- Člen | 98
Je to trochu odlišná filosofie. Ale přijde mi přehlednější, když jsou věci dány víc explicitně než implicitně. Routování, kdy se metoda presenteru nastavuje podle „proměnné“ v url mi přijde prostě příliš magická a nepřehledná. Radši bych u routy okamžitě viděl, který presenter ji zpracovává, než „routoval“ v hlavě.
Pojmenování rout má výhodu, že je to jednoduché na zapamatování což se vyplatí při zpětném vytváření odkazů. Takhle si člověk musí pamatovat jak se jmenuje view a presenter, takže zase routovat v hlavě :-)
Když se bude routa jmenovat stále stejně tak může změnit presenter i svoji url (vše definováno na jednom místě u definici routy – DRY) a nemusí se měnit parametry funkce link().
Jak bys třeba řešil vícejazyčné routy? tak aby /article i /clanek vedlo na stejný presenter?
Ale jak říkám, třeba časem implementuji takovýto router, protože Nette je tak dobře navrženo, že výměnu routeru umožňuje.
- David Grudl
- Nette Core | 8218
V jiných systémem má routování obrovských význam, naopak v Nette se jím netřeba zabývat. To je věc, kterou řeším často až když je aplikace hotová. Odsud zřejmě pramení to nepochopení.
Unitř aplikace se odkazuje tak, jako když volám metody v OOP:
Presenter::view($arg1, $arg2)
. Konrétně třeba
Product:detail($id)
. Volám metodu detail
třídy
Product
a předám ji parametr $id
. Připadá mi to
maximálně srozumitelné a nevím, proč do toho zanášet další
identifikátor routy – je zbytečný.
Pomiňme detaily, jako že zápis Product:detail(...)
vede na
ProductPresenter::renderDetail(...)
– obojí lze totiž změnit
úpravou metod formatPresenterClass, formatRenderMethod a zavedením
anotoací.
- David Grudl
- Nette Core | 8218
veena napsal(a):
Jak bys třeba řešil vícejazyčné routy? tak aby /article i /clanek vedlo na stejný presenter?
$router[] = new Route('/article/<id>', array(
'presenter' => 'Article',
'lang' => 'en',
));
$router[] = new Route('/clanek/<id>', array(
'presenter' => 'Article',
'lang' => 'cs',
));
Presenter:
class ArticlePresenter extends Presenter
{
public function renderDefault($id, $lang)
{
// pseudokód
SELECT text FROM table WHERE id = $id AND lang = $lang
$template->lang = $lang;
$template->article = $text;
}
}
Šablona:
<html lang="{$lang}">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="{$lang}">
</head>
<body>
{!article}
</body>
</html>
- veena
- Člen | 98
David Grudl napsal(a):
V jiných systémem má routování obrovských význam, naopak v Nette se jím netřeba zabývat. To je věc, kterou řeším často až když je aplikace hotová. Odsud zřejmě pramení to nepochopení.
Unitř aplikace se odkazuje tak, jako když volám metody v OOP:
Presenter::view($arg1, $arg2)
. Konrétně třebaProduct:detail($id)
. Volám metodudetail
třídyProduct
a předám ji parametr$id
. Připadá mi to maximálně srozumitelné a nevím, proč do toho zanášet další identifikátor routy – je zbytečný.
Pomiňme detaily, jako že zápis
Product:detail(...)
vede naProductPresenter::renderDetail(...)
– obojí lze totiž změnit úpravou metod formatPresenterClass, formatRenderMethod a zavedením anotoací.
Snad rozumím. Myslím to tak, že identifikátor routy se podle mě hodí pro zpětné vytvoření url v šabloně. V presenteru se samozřejmě nepoužívá. Tedy že by se nějak předávalo do parametrů. Jen může také existovat nějaká funkce, která url získá reversně ze jména pokud je třeba potřeba k přesměrování apod.
Zkusím ale popřemýšlet o tvém více programátorském přístupu, kdy hlavní, neměnné a podstatn0 jsou názvy presenterů a view.
Dík za ukázku vícejazyčných rout. Je mi to jasnější. Tohle je explicitní dost.
Mě matou případy, kdy je presenter a view dynamicky generováno podle cesty v url. Ale to je holt můj problém :-)
- David Grudl
- Nette Core | 8218
Snad rozumím. Myslím to tak, že identifikátor routy se podle mě hodí pro zpětné vytvoření url v šabloně.
Ony se také v šabloně velmi často vytvářejí (příklad s využitím filtru curlyBrackets):
...<a href="{$presenter->link('edit', 10)}">self::edit(10)</a>...
...<a href="{$presenter->link('Product:list')}">Product::list()</a>...
Experimentálně lze využít i filtr netteLinks:
...<a href="nette:edit?id=10">self::edit(10)</a>...
...<a href="nette:Product:list">Product::list()</a>...
…Tedy že by se nějak předávalo do parametrů. Jen může také existovat nějaká funkce, která url získá reversně ze jména pokud je třeba potřeba k přesměrování apod.
URL generuje v presenteru (a komponentě) funkce
$this->link('edit', 10)
– tedy stejně jako v šabloně. Lze
vygenerovat URL sám na sebe $this->backlink()
.
K přesměrování slouží $this->redirect(...)
, k přechodu
na jiný presenter/view $this->forward(...)
.
Mě matou případy, kdy je presenter a view dynamicky generováno podle cesty v url. Ale to je holt můj problém :-)
Což se v praxi stejně tak moc nepoužívá…
- veena
- Člen | 98
Dobře, snad jsem už pochopil tvoji filosofii routování :-)
I když nebudeme uvažovat pojmenované routy, tak tu mám návrh na jednu možnost zápisu.
Pokud presenter a view jsou vždy povinné hodnoty, tak bych je vyřadil ze společného pole s ostatními parametry a dal jako samostatné hodnoty.
$router = $application->getRouter(array(
array('index.php', 'default', 'default'),
array('<presenter>/<action>/<id>', 'default', 'default', array('id' => NULL), Route::ONE_WAY),
# případně již teď používat syntaxi namespace
array('<presenter>/<action>/<id>', 'default:default', array('id' => NULL), Route::ONE_WAY),
... další seznam rout ...
));
- David Grudl
- Nette Core | 8218
View není potřeba uvádět vůbec – výchozí hodnota je ‚default‘ zcela automaticky.
Presenter jako extra parametr? Popřemýšlím.
Jinak by to v podstatě šlo, jen takto:
$router = $application->getRouter(array(
new Route('index.php', 'default'),
new Route('<presenter>/<action>/<id>', 'default', array('id' => NULL), Route::ONE_WAY),
...
));
To vytvoření objektu je skutečně klíčové.
Můžeš si to vyzkoušet hned (bez extra presenteru), změň řádky v Application/Application.php takto:
public function getRouter($init = NULL) // přidat $init = NULL
{
if ($this->router === NULL) {
$this->router = new MultiRouter($init); // přidat $init
- David Grudl
- Nette Core | 8218
Routa s index.php
určuje, že při požadavku na stránku
index.php
se otevře presenter default
a view
default
. Lze ji použít pro zpětnou kompatibilitu – pokud na
web již existují odkazy ve tvaru http://example.com/index.php
, je
vhodné je podchytit.
U zpětně-kompatibilních rout se nastavuje příznak Route::ONE_WAY
(jednosměrka), který zajistí, že routa může požadavek přijmou (stránka
index.php funguje), ale nevytváří URL. Tedy při generování URL pro
presenter default
a view default
se nevygeneruje
index.php
, ale použije se další routa.
V případě nenalezení routy se vyhodí výjimka. Je možné definovat presenter (v proměnné $application->errorPresenter), který se v takovém případě zavolá.
- ViliamKopecky
- Nette hipster | 230
V případě nenalezení routy se vyhodí výjimka. Je možné definovat presenter (v proměnné $application->errorPresenter), který se v takovém případě zavolá.
Áha, to je chytřeší (samozřejmě), já to obcházel přes zachytávání FileNotFoundException, což mi bylo jasný že neni to pravé ořechové…
Jinak v errorPresenteru se získá kód chyby (případně další věci) jak?
- David Grudl
- Nette Core | 8218
errorPresenter dostane parametr exception s vyhozenou výjimkou. Je možné ho získat třeba takto:
public function renderDefault($exception)
{
...
}
nebo
public function renderDefault()
{
$exception = $this->params['exception'];
// nebo:
$exception = $this->request->params['exception'];
}
- Ondřej Brejla
- Člen | 746
Kenn napsal(a):
Nejspíš stupidní dotaz, ale potřebuji pro funkční routování něco upravovat v souboru htaccess nebo přidat nějaké zjišťování parametrů z URL? Mám nadefinované routy, vytvoří se pěkná adresa, ale vyplivne to 404ku.
Je potřeba .htaccess…viz 646-htaccess
- kravčo
- Člen | 721
Kenn napsal(a):
.htaccss mám správně, takže chyba bude nejspíš někde jinde. No nic, asi prozatím routování oželím.
EDIT: chyba byla v nastavení apache :-)
Pre budúcnosť, odhaľovať chyby routovania pomáha RoutingDebugger.
Editoval kravco (27. 1. 2009 16:54)