návrh rout

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Kenn
Člen | 110
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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)

Honza Marek
Člen | 1664
+
0
-

Zkus foo parametry

Editoval Honza M. (15. 5. 2009 20:39)

David Grudl
Nette Core | 8139
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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');