viceurovnove routy

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

Zdravim,

budu pracovat na jednom projektu, ktery ale pocita tak trochu s jinyma routama, nez nette(a vubec vetsina frameworku). Kam se podivam, tak je to <presenter>/<action>, nebo na podobny zpusob a pokud by chtel clovek web, ze by mel /o-nas/, /katalog/ a dalsi veci, tak je to psani XY stejnych rout („o-nas“, array(„presenter“ ⇒ „stranky“,„view“ ⇒ „oNas“)). O to horsi situace prijde, kdyz je pozadavek na viceurovnove adresy – /o-nas/kontakt/kontaktni-formular/.
To by se jeste dalo zvladnou pridanim rout pro kazdou stranku. Ale pridanim vicejazycnosti to pridava vic a vic na objemu (/en/about-us/contact/contact-form/).

Pak je tady moznost adresy /katalog/strana-2/ a /katalog/hodinky/strana-2, popr /katalog/hodinky/digitalni/(strana-*).

Tolik k uvodu :) Zatim jsem se teoreticky dopracoval k tomu, ze budu muset kompletne prepsat router s tim, ze kazda stranka(tahana z databaze podle sve casti url) bude mit nastaveny modul. Kazdy modul pak bude mit nastavene routy, kterych muze nabyvat(v pripade katalogu napr)

array(
"strana-:str" => "funkcePresenteruKZavolani",
":kategorie+" => "funkcePresenteruKZavolani",
)

a po zjisteni stranky, ktera se ma zobrazit se teprv parsuje adresa pro modul stranky ke zjisteni, co vlastne zobrazit.

Takze driv, nez se do toho pustim prakticky, chci se zeptat, jestli jestli jste to treba nekdo uz podobne neresil a nenavedl me na spravnou cestu.

pmg
Člen | 372
+
0
-

Kam se podivam, tak je to <presenter>/<action>, nebo na podobny zpusob a pokud by chtel clovek web, ze by mel /o-nas/, /katalog/ a dalsi veci, tak je to psani XY stejnych rout („o-nas“, array(„presenter“ ⇒ „stranky“,„view“ ⇒ „oNas“)).

V tomto případě není potřeba psát routu pro každou stránku. Stačí tato routička:

$router[] = new Route('<action>', array(
	'presenter' => 'stranky',
));

Fígl je v tom, že třída Route podporuje systém vstupních a výstupních filtrů, z nichž převaděč na camelCase je výchozí.

Obtížně se routa vytváří např. tehdy, když jsou povinné parametry napravo od nepovinných, jako v tom dalším příkladu. Není ale obtížné si pro takový případ definovat vlastní router.

Router je koncipován tak, aby tvořil pěkné adresy ve většině případů automaticky. Pokud máš ale specifické požadavky, nikdo ti nebrání chování změnit.

Mas3r
Člen | 116
+
0
-

Přesně tak jsem přemýšlel i já a nakonec se povedlo. Použil jsem aliasy ( takové i-nody :) ) K tomu však bylo nutné základní Route.php předělat a vzniknul z něj „hybrid“ – zvládá jak <lang>/<module>/<presenter>/<action>/… tak i cs/notebooky/podle-hmotnosti/do-2kg, kde lang je nepovinný. A pro minimální náročnost na DB to ještě kešuju. Netuším jaká HW náročnost tam bude třeba při 99999999 aliasech, to by se muselo zkusit.

Tak tedy doporučuju si projít tentou router a trošku se v tom prohrabat.

Ola
Člen | 385
+
0
-

A co takto?

V routeru:

$router[] = new Route('<page .+?>', array(
    'presenter' => 'Page',
    'action' => 'default',
));

A v presenteru dát explode dle lomítek .. ale tahle routa by měla být jako poslední ..

pmg
Člen | 372
+
0
-

Už jsem to měl taky na jazyku, ale pak by bylo nutné udělat ještě funkci, která z parametrů zpětně vytvoří URL. Šlo by použít funkce loadState a saveState (viz příklad Fifteen), ale koncepčně je lepší routování řešit routerem.

Jod
Člen | 701
+
0
-

Taký router by som aj ja potreboval. Potešilo by keby sa objavil v sekcii typy a triky =)

Mas3r
Člen | 116
+
0
-

Ola napsal(a):

A co takto?

V routeru:

$router[] = new Route('<page .+?>', array(
    'presenter' => 'Page',
    'action' => 'default',
));

A v presenteru dát explode dle lomítek .. ale tahle routa by měla být jako poslední ..

Bohužel u tohoto řešení nemůžeš dát pak už žádné nepovinné parametry jako jazyk atd

Editoval Mas3r (4. 12. 2008 10:08)

insider
Člen | 31
+
0
-

Diky za objasneni s tim <action> a prevedenim se na camel case, to by ulehcilo praci, kdyby tam nebyly ty dalsi pozadavky :(

Mas3r napsal(a):


Tak tedy doporučuju si projít tentou router a trošku se v tom prohrabat.

Ktery router si tim myslel?

David Grudl
Nette Core | 8228
+
0
-

V Nette nejsou schopnosti routování nijak omezené.

Z programátorského hlediska je router vše, co implementuje rozhraní IRouter o dvou metodách – parsuj URL a generuj URL. Framework se dodává s routery, které umí řešit nejčastější situace, pro ty ostatní si napíši router vlastní, což však není nic složitého (nejlépe začnu tak, že zkopíruju a upravím SimpleRouter).

Pár příkladů URL a na jaký presenter:view se mapují:

  • https://doc.nette.org/cs/View:default, id="dokumentace", lang="cs"
  • https://doc.nette.org/cs/View:default, id="dokumentace", lang="cs"
  • https://doc.nette.org/cs/?action=EditEdit:default, id="dokumentace", lang="cs"
  • https://nette.org/cs/meta/menuView:default, id="meta/menu", lang="cs"
  • http://www.vitalita.cz/fat-absorberProduct:default, id="fat-absorber"
  • http://www.vitalita.cz/hubnutiCategory:default, id="hubnuti"
  • http://www.vitalita.cz/kontaktArticle:default, id="kontakt"

Oba weby si vystačí se sedmi routy (shodou okolností stejný počet) a to včetně rout pro XML export nebo download souborů. Web Vitalita má navíc vlastní routu, která rozhodne o presenteru náhledem do databázových tabulek.

Snažím se tím ukázat, jaká je filosofie frameworku. Nejde o boj se třídou Route nebo o „přepsání routeru“. Naopak o velmi svobodnou oblast.

soundake
Člen | 24
+
0
-

David Grudl napsal(a):

Snažím se tím ukázat, jaká je filosofie frameworku. Nejde o boj se třídou Route nebo o „přepsání routeru“. Naopak o velmi svobodnou oblast.

V tomhle bude asi problém třeba u mě, jelikož ani asi na pátý pokus jsem nedokázal nějak tu filosofii routování v Nette pochopit. Pokaždé to dělalo něco jiného než bych potřeboval a přitom nejde o žádné harakiri. Zatím to teda odkládám a vyvíjím na klasice <presenter>/<action>/<id>.

Pro mě, jako pro programátorskou lamu, by to chtělo asi víc polopatičtější dokumentaci s více příklady. A možná nejen pro mě :)

romansklenar
Člen | 655
+
0
-

V tom příkladu s vitalita.cz jde tedy o tebou modifikovaný router nebo jde o Route? Nedokážu si představit jak by ta routa vypadala, respektive jak by rozezná, že fat-absorber vede na Product:default a hubnuti vede zase na jiný presenter Category:default. Nevadilo by ti uvést i příklad přímo definice té routy?

soundake napsal(a):

Pro mě, jako pro programátorskou lamu, by to chtělo asi víc polopatičtější dokumentaci s více příklady. A možná nejen pro mě :)

Nejsi sám, v definicích rout mám taky mezerky a nevím co všechno ještě předemnou skývají = viz výše.

Editoval romansklenar (4. 12. 2008 13:05)

David Grudl
Nette Core | 8228
+
0
-
/**
 * Special route.
 *
 * @author     David Grudl
 * @package    Vitalita
 */
class ShopRoute extends Object implements IRouter
{

	/**
	 * Maps HTTP request to a PresenterRequest object.
	 * @param  Nette\Web\IHttpRequest
	 * @return PresenterRequest|NULL
	 */
	public function match(IHttpRequest $context)
	{
		if (!preg_match('#^/([a-zA-Z0-9-]+)/?$#', $context->getUri()->path, $matches)) {
			return NULL;
		}

		$id = $matches[1];
		if (dibi::fetchSingle("SELECT COUNT(*) FROM tabulka_produktu WHERE nameid=%s", $id)) {
			$presenter = 'Front:Product';

		} elseif (dibi::fetchSingle("SELECT COUNT(*) FROM tabulka_kategorie WHERE nameid=%s", $id)) {
			$presenter = 'Front:Category';

		} elseif (dibi::fetchSingle("SELECT COUNT(*) FROM tabulka_s_clanky WHERE nameid=%s", $id)) {
			$presenter = 'Front:Article';

		} else {
			return NULL;
		}
		// alternativa: použít jednu tabulku s páry URL -> jméno Presenteru
		// výhoda: jeden lookup místo (až) tří, neměřitelně vyšší rychlost ;)
		// nevýhoda: nutnost ji udržovat :-(

		// alternativa č.2: místo COUNT(*) načíst z DB celý záznam a předat v parametru presenteru
		// výhoda: stejně jej bude potřebovat
		// nevýhoda: nadstandardní závislost mezi routerem a presenterem

		$params = $context->getQuery();
		$params['id'] = $id;

		return new PresenterRequest(
			$presenter,
			$context->getMethod(),
			$params,
			$context->getPost(),
			$context->getFiles(),
			array('secured' => $context->isSecured())
		);
	}



	/**
	 * Constructs URL path from PresenterRequest object.
	 * @param  Nette\Web\IHttpRequest
	 * @param  PresenterRequest
	 * @return string|NULL
	 */
	public function constructUrl(PresenterRequest $request, IHttpRequest $context)
	{
		// overime ze presenter je jeden ze podporovanych a existuje parameter 'id'
		static $presenters = array(
			'Front:Spot' => TRUE,
			'Front:Category' => TRUE,
			'Front:Product' => TRUE,
		);

		if (!isset($presenters[$request->getPresenterName()])) {
			return NULL;
		}

		$params = $request->getParams();
		if (!isset($params['id'])) {
			return NULL;
		}

		// vse ok, generuj URL
		$uri = $context->getUri()->basePath . rawurlencode($params['id']);
		unset($params['id'], $params['action']);

		$query = http_build_query($params, '', '&');
		if ($query !== '') $uri .= '?' . $query;

		return $uri;
	}

}
David Grudl
Nette Core | 8228
+
0
-

soundake napsal(a):

V tomhle bude asi problém třeba u mě, jelikož ani asi na pátý pokus jsem nedokázal nějak tu filosofii routování v Nette pochopit.

Filosofie je v tom, že všechno nestojí a nepadá se třídami Route a SimpleRouter. Naučit se správně používat Route je něco docela jiného. To chce praxi. Sám si říkám, že tak za rok bych v tom mohl být už docela dobrý ;)

romansklenar napsal(a):

Nevadilo by ti uvést i příklad přímo definice té routy?

Dělám to nerad (neboť to, že programuju open-source, neznamená, že vše co naprogramuju bude taky open-source), ale máš ji mít :-)

romansklenar
Člen | 655
+
0
-

David Grudl napsal(a):

Filosofie je v tom, že všechno nestojí a nepadá se třídami Route a SimpleRouter. Naučit se správně používat Route je něco docela jiného. To chce praxi. Sám si říkám, že tak za rok bych v tom mohl být už docela dobrý ;)

Díky! Příklad nad tisíce slov ;) přesně toto jsem potřeboval, abych si to v hlavě urovnal a ostatním to určitě taky pomůže s pochopením filosofie routování.

Dělám to nerad (neboť to, že programuju open-source, neznamená, že vše co naprogramuju bude taky open-source), ale máš ji mít :-)

Tak to pro teďkom ber jako propagaci a ukázku možností routování, jsem ti za tuhle ukázku vďěčný :)

Mas3r
Člen | 116
+
0
-

Když už jsme u těch rout:

Jak donutit Nette zpracovat toto:

<?php
$router[] = array('<lang>/admin/<presenter>....', '....');

$router[] = array('<page ?*.>', '<= ted nevim jestli je ten reg. vyraz spravny');
?>

Aby Nette poznalo, že při požadavku /cs/neco/neco vybrala druhou routu. Zkoušel jsem to, ale nešlo mi to.

romansklenar
Člen | 655
+
0
-

Zkus dát pryč ten validační výraz ?*., pak by ti to podle mě mělo automaticky skákat na tu druhou routu, když mezi druhým a třetím lomítkem nebude admin.

A nebo zkus spíše .* pokud chceš akceptovat lomítka v masce, ne?

Nebo možná další možnost: .*[\/]?

Editoval romansklenar (4. 12. 2008 14:08)

insider
Člen | 31
+
0
-

Jak uz bylo receno, priklad za 1000 slov. Diky. Hned mi to vnuklo predstavu, jak bych to mel udelat.

Mas3r
Člen | 116
+
0
-
<?php

$router[] = new Route(
	'<lang (cs|de|en)>/admin/<presenter>/<action>/<id>',
	array(
		'lang' => 'cs',
		'module' => 'Admin',
		'presenter' => 'Default',
		'action' => 'default',
		'id' => NULL
	)
);

$router[] = new Route(
	'<lang (cs|de|en)>/<module>/<presenter>/<action>/<id>',
	array(
		'lang' => 'cs',
		'module' => 'Default',
		'presenter' => 'Default',
		'action' => 'default',
		'id' => NULL,
	)
);

?>

Při tomto použití i adresa „example.com/“ vede na první routu. Možná by bylo lepší, kdyby to zjistilo, zda není v patternu routeru nějaká část adresy „natvrdo“, protože teď to nebere v potaz to, že by v adrese mělo být „/admin/“

Editoval Mas3r (4. 12. 2008 14:55)

David Grudl
Nette Core | 8228
+
0
-

Nedávej parametr lang jako volitelný.

(jen detail – je možné vynechat závorky a používat i <lang cs|de|en>)