návrh rout
- Kenn
- Člen | 110
Předělávám si svůj blog na Nette, čistě abych se naučil a řeším menší problém s routama.
<?php
$router[] = new Route('<clanek>', array(
'presenter' => 'Default',
'view' => 'clanek',
'clanek' => NULL,
));
$router[] = new Route('strana/<strana>', array(
'presenter' => 'Default',
'view' => 'default',
'strana' => 'strana',
));
$router[] = new Route('kategorie/<kategorie>', array(
'presenter' => 'Default',
'view' => 'default',
'kategorie' => 'kategorie',
));
?>
Takhle je mám nadefinované, jenže routy na stránku a kategorii se mi bijou. Výsledek je, že routa na stránku funguje (je první), ale routa na kategorii odkazuje strana/?kategorie=kategorie. Měl by někdo nějaký nápad jak routy nadefinvoat, aby fungovaly takhle jednoduše a nebily se mezi sebou?
Stránkování u kategorie asi budu muset pořešit přímo skriptem že?
- Tomik
- Nette Evangelist | 485
Zdravím.
Otázka zní, jaký link voláš (myslím ten link, který ti generuje to url, co píšeš).
Bez ohledu na to si ale myslím, že by bylo lepší si vytvořit více Presenterů pro každou sekci webu (stránka, clanky, kategorie) jednu. Pak se ti to bít nebude.
Co se týče stránkování, použij komponentu Paginator.
- rokerkony
- Člen | 122
nebo by to melo stacit to prohodit ne?
<?php
$router[] = new Route('strana/<strana>', array(
'presenter' => 'Default',
'view' => 'default',
'strana' => 'strana',
));
$router[] = new Route('kategorie/<kategorie>', array(
'presenter' => 'Default',
'view' => 'default',
'kategorie' => 'kategorie',
));
$router[] = new Route('<clanek>', array(
'presenter' => 'Default',
'view' => 'clanek',
'clanek' => NULL,
));
?>
protoze se ti prvne bere prvni routa a ta sezere i /stranka/neco i kdyz bys
to chtel brat az tou druhou :)
dobre je pouzit RoutingDebugger :-)
- kravčo
- Člen | 721
Na prvý pohľad by som tipol, že problém vyvstane pri generovaní
odkazu – totiž odkaz na kategóriu
Default:default?kategoria=nette
má rovnaký cieľový prezenter,
ako odkaz na stránku Default:default?stranka=kontakt
. Keďže sa
ide poporadí, použije sa skoršia routa (a použije sa predvolený parameter
strana).
Vyriešiť by to mohli samostatné prezentery prezentery/akcie pre každú routu, napr.:
Default:clanok ; toto je rovnaké ako to máš
Default:stranka
Default:kategoria
; prípadne
Clanok:default
Stranka:default
Kategoria:default
Z môjho pohľadu je takéto členie i logické, keďže každá z vecí má odlišnú logiku.
ps> od verzie 0.9 (počínajúc r303) budeš musieť v predvolených parametroch namiesto „view“ písať „action“
- Kenn
- Člen | 110
Do toho rozdělení se mi právě moc nechce (proto se taky ptám :-)), protože mám stejnou šablonu, stejný dotaz na databázi, akorát jiné podmínky v tom dotazu.
Linky vypadají takto:
<?php
{link this, 'kategorie' => $clanek->seokategorie}
{link this, 'strana' => $paginator->page - 1}
?>
Prohození rout nefunguje, vždycky se použije ta routa, která je první, což je logické. Ideální by bylo, kdyby šla u odkazu přiřadit i routa, která se má použít, ale to asi nejde co?
- _Martin_
- Generous Backer | 679
Ty routy se dají nastavit, aby se vzájemně vylučovaly:
$router[] = new Route('strana/<strana>', array(
'presenter' => 'Homepage',
'view' => 'default',
'strana' => 'strana',
'kategorie' => NULL,
));
$router[] = new Route('kategorie/<kategorie>', array(
'presenter' => 'Homepage',
'view' => 'default',
'kategorie' => 'kategorie',
'strana' => NULL,
));
Nevýhoda je, že potom nemůžeš vytvořit odkaz na „kategorie/“,
protože {link default 'kategorie' => NULL}
požere i první
routa. Osobně bych to viděl na změnu implementace, tvé řešení se zdá
být nešťastným (logicky jsem pro rozdělení na dvě akce, které mohou
sdílet společné části v metodách render
– stačí
použít metoda setView
).
- kravčo
- Člen | 721
Potom by to mohlo vyriešiť odstránenie predvolených hodnôt parametrov z rout (ktoré majú teraz aj tak dosť nezmyselné hodnoty):
$router[] = new Route('strana/<strana>', array(
'presenter' => 'Default',
'action' => 'default', // všimni si zmenu 'view' → 'action'
));
$router[] = new Route('kategorie/<kategorie>', array(
'presenter' => 'Default',
'action' => 'default', // všimni si zmenu 'view' → 'action'
));
Takto sa stanú povinnými a generovanie odkazov by mohlo fungovať podľa tvojich predstáv.
Ale, viac by mi to dávalo zmysel, ak by to stránkovanie malo fungovať za každých okolností – či mám vybratú kategóriu alebo nie. Teda boli by prípustné odkazy:
; 2. strana zoznamu všetkých
Default:default?strana=2
; (predvolená) 1. strana zoznamu kategórie nette
Default:default?kategoria=nette
; 2. strana zoznamu kategórie zend
Default:default?kategoria=zend&strana=2
V tom prípade by som tie dve routy spojil do jednej:
$router[] = new Route('kategorie/<kategorie> ? <strana>', array(
'presenter' => 'Default',
'action' => 'default', // všimni si zmenu „view“ → „action“
'kategorie' => 'all', // štandardne chceme všetky
'strana' => 1, // štandardne prvú stránku
));
- Kenn
- Člen | 110
Nakonec jsem to vyřešil nějak takhle
<?php
$router[] = new Route('strana/<strana>', array(
'presenter' => 'Default',
'action' => 'default',
));
$router[] = new Route('kategorie/<kategorie>', array(
'presenter' => 'Default',
'action' => 'default',
));
$router[] = new Route('kategorie/<kategorie>/strana/<strana>', array(
'presenter' => 'Default',
'action' => 'default',
));
?>
Kupodivu vše funguje jak má a přesně jak jsem chtěl. Děkuji moc :-)
- 2bfree
- Člen | 248
Poprosil byc o radu při návrhu rout.
Je možné přesvědčit NETTE tak, aby provozoval SEO friendly URL (cool url)
jinak nez example.com/controler/view
Momentálně mám stránky, na kterých je vše za doménou zpracováváno controlerem Article, kterému chci předávat odkazy v následujících podobách:
- example.com/article-name.html
- example.com/category/article-name.html
- example.com/category/subcategory/article-name.html
S tím že tyto 3 parametry by se předávali controleru Article, podle kterých by tento vracel konkrétní článek.
Zkousel jsem psi kusy a nakonec jsem neuspel. Vyzkousel jsem toto a nevim kde je chyba:
<?php
$router = $application->getRouter();
$router[] = new Route('<category>/<subcategory>.html', array(
'presenter' => 'Default',
'action' => 'createTemplate',
'category' => NULL,
'subcategory' => NULL,
));
$router[] = new Route('<category>.html', array(
'presenter' => 'Default',
'action' => 'createTemplate',
'category' => NULL,
));
?>
Subcategory funguji, ale jakmile tomu dam odkaz domena.cz/test.html tak se za to doplni lomitko a nefunguje to.
Zkousel jsem je i prohodit, ale vznikne z toho domena.cz/clanek.html a zbytek prida za ? jako normalni parametry.
Jeste se zeptam, jak ma vypadat spravny .htaccess, aby ty routy fungovaly.
Editoval 2bfree (15. 5. 2009 19:45)
- David Grudl
- Nette Core | 8218
V té první routě by neměly být parametry category
a
subcategory
volitelné, takže odstraň to
'category' => NULL
a 'subcategory' => NULL,
.
V druhé routě nejspíš taky.
Jak má vypadat .htaccess najdeš v tools/Skeleton
nebo v
examples
.
- Jan Tvrdík
- Nette guru | 2595
2bfree napsal(a): example.com/category/subcategory/article-name.html
Podle mě není možné v Nette řešit takovéto URL čistě bez napsání vlastního Routeru. (Poté co si napíšeš svůj první router už budeš ty ostatní napíšeš během chvíle.)
Editoval Jan Tvrdík (15. 5. 2009 21:09)
- Ondřej Mirtes
- Člen | 1536
Jan Tvrdík napsal(a):
2bfree napsal(a): example.com/category/subcategory/article-name.html
Podle mě není možné v Nette řešit takovéto URL čistě bez napsání vlastního Routeru. (Poté co si napíšeš svůj první router už budeš ty ostatní napíšeš během chvíle.)
Proč by měl být tento tvar adresy problém? Mně naopak přijde, že Router v Nette je skoro všemocný (pokud po něm nechci nějakou užší spolupráci s databází) a tento tvar adresy je dost základní.
- Honza Marek
- Člen | 1664
Já si vyrobil router, který zvládá adresy typu example.com/stranka/podstranka/podstrankapodstranky/takystranka v libovolném zanoření a presenteru vyplivává jen id poslední stránky. Musim říct, že je to mnohem lepší než pro každé zanoření třeba dělat speciální routu a pak ty stránky dohledávat v presenteru.
- Jan Tvrdík
- Nette guru | 2595
LastHunter napsal(a):
Proč by měl být tento tvar adresy problém?
Nelze to udělat čistě, tj. presenter dostane řetězec
muj-clanek
místo ID článku. Při generování odkazu nelze
použít čisté {plink Clanky:zobrazit $idClanku}
(kde
$idClanku
je např. 7), ale musí se použít škaredé
{plink Clanky:zobrazit $urlClanku
} (kde $urlClanku
je
např. muj-clanek
).
- Jod
- Člen | 701
Dá sa to aj bez routru, ja som si to zatiaľ ulahčil tak, že posielam adresu priamo do akcie kde sa spracuje nejak takto (trochu prasácky :D ):
function getPageByPath($path)
{
$p = explode('/', $path);
$query = array();
$this->subQuery($query, $p, count($p) - 1);
$page = dibi::fetch($query);
return $page;
}
private function subQuery(&$query, $path, $depth, $cols = null)
{
if(is_null($cols)) {
$cols = (count($path) - 1 === $depth) ? '*' : 'id';
}
array_push($query, '(SELECT ' . $cols . ' FROM [Pages] WHERE url=%s AND [languageId]=%i AND [active]=1', $path[$depth], self::$languageId);
$depth = $depth - 1;
if($depth < 0) {
array_push($query, ' AND pageId IS NULL');
}
else {
array_push($query, ' AND pageId=');
$this->subQuery($query, $path, $depth);
}
array_push($query, ' limit 0, 1)');
}
Výhodou je, že sa vykonáva len jeden select, aj keď sa to rekurzívne zanoruje, tak vzniká viac subselectov.
Ukážka je na www.owebe.eu
Editoval Jod (16. 5. 2009 0:58)
- cuga
- Člen | 210
Honza M. napsal(a):
Já si vyrobil router, který zvládá adresy typu example.com/stranka/podstranka/podstrankapodstranky/takystranka v libovolném zanoření a presenteru vyplivává jen id poslední stránky. Musim říct, že je to mnohem lepší než pro každé zanoření třeba dělat speciální routu a pak ty stránky dohledávat v presenteru.
Tohle bych taky ocenil :) zkusil jsem rozchodit ShopRoute, kterou David nekde nahodil, ale bez uspechu…
- Honza Marek
- Člen | 1664
Tohle bych taky ocenil :) zkusil jsem rozchodit ShopRoute, kterou David nekde nahodil, ale bez uspechu…
Nj, tomu rozumim. Ale je to jedna z věcí, co jsem do nette vyrobil, kterých si cením nejvíc, tak se mi to nechce moc rozdávat :)
- _Martin_
- Generous Backer | 679
2bfree napsal(a):
Poprosil byc o radu při návrhu rout.
Stačí-li ti použítí běžného Routeru, dám ti radu následující:
$router[] = new Route('<article>.html', array(
'presenter' => 'Article',
'action' => 'default',
'category' => NULL,
'subcategory' => NULL,
));
$router[] = new Route('<category>/<article>.html', array(
'presenter' => 'Article',
'action' => 'default',
'subcategory' => NULL,
'article' => NULL,
));
$router[] = new Route('<category>/<subcategory>/<article>.html', array(
'presenter' => 'Article',
'action' => 'default',
'article' => NULL,
));
S těmito routami můžeš mít URL:
- /clanek.html
- /kategorie/
- /kategorie/clanek.html
- /kategorie/podkategorie/
- /kategorie/podkategorie/clanek.html
Akorát v metodě actionDefault
musíš ověřovat, jaké
parametry se předávají a podle toho zvolit šablonu pro výpis (zda
vykresluješ článek či seznam článků) a sestavit správně dotazy
do DB.
public function renderDefault($article, $category = NULL, $subcategory = NULL)
{
// ... kód si doplň sám
}
S takhle definovanou metodou lzde odkazy v šabloně vytvářet jednoduše:
{link Article: 'uvod'}
{link Article: 'kontakt'}
{link Article: 'historie', 'o-firme'}
{link Article: 'lednicka', 'elektronika', 'spotrebice'}
- Jan Tvrdík
- Nette guru | 2595
cuga napsal(a):
Tohle bych taky ocenil :) zkusil jsem rozchodit ShopRoute, kterou David nekde nahodil, ale bez uspechu…
Jak jsem psal výše, tak první vlastní Router jsem psal metodou pokus omyl, ale další se už pak píší snadno. Mám v plánu (někdy až budu mít čas) napsat obecný SEO router a postnout ho sem, ale zatím jsem se k tomu nedostal.
Zatím alespoň ukázka jednoduchého SEO routeru (zkopírováno bez úprav z disku):
<?php
class SpecialOffersRouter implements IRouter
{
private $pathPrefix;
private $presenter;
private $action;
public function __construct($pathPrefix, $presenter, $action)
{
$this->pathPrefix = $pathPrefix;
$this->presenter = $presenter;
$this->action = $action;
}
public function match(IHttpRequest $httpRequest)
{
$path = $httpRequest->getUri()->relativeUri;
if (!preg_match('#^' . preg_quote($this->pathPrefix, '#') . '(.+)$#', $path, $matches)) {
return NULL;
}
$url = & $matches[1];
$query = dibi::query('
SELECT [special_offers].[id]
FROM [special_offers]
WHERE [url] = %s', $url
);
if ($query->rowCount() === 0) {
return NULL;
}
$offerId = $query->fetchSingle();
$params = array();
$params += $httpRequest->getQuery();
$params['action'] = $this->action;
$params['offerId'] = $offerId;
return new PresenterRequest(
$this->presenter,
$httpRequest->getMethod(),
$params,
$httpRequest->getPost(),
$httpRequest->getFiles(),
array('secured' => $httpRequest->isSecured())
);
}
public function constructUrl(PresenterRequest $request, IHttpRequest $context)
{
if ($request->getPresenterName() !== $this->presenter) {
return NULL;
}
$params = $request->getParams();
if ($params['action'] !== $this->action) {
return NULL;
}
$offerId = $params['offerId'];
$path = dibi::query('
SELECT [special_offers].[url]
FROM [special_offers]
WHERE [special_offers].[id] = %i', $params['offerId']
)->fetchSingle();
if ($path === FALSE) {
return NULL;
}
unset($params['action']);
unset($params['offerId']);
$uri = new UriScript($context->getUri()->scheme . '://' . $context->getUri()->authority . $context->getUri()->basePath);
$uri->path .= $this->pathPrefix . $path;
$uri->query = $query = http_build_query($params, '', '&');
return $uri->getAbsoluteUri();
}
}
a v bootstrapu:
$router[] = new SpecialOffersRouter('akcni-nabidky/', 'Front:SpecialOffers', 'view');