Routování jednourovňové adresy

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

Zdravím,

//produkt
$router[] = new Route('<url>/', array(
  'module' => 'Front',
  'presenter' => 'Product',
  'action' => 'Default',
  'url'=>null
));

//categorie
$router[] = new Route('<url>/', array(
  'module' => 'Front',
  'presenter' => 'Category',
  'action' => 'Default',
  'url'=>null
));

//novinky
$router[] = new Route('<url>/', array(
  'module' => 'Front',
  'presenter' => 'News',
  'action' => 'Default',
  'url'=>null
));

pokud to dojde do prvního presenteru a url neexistuje v db, chci to redirecnout na další routu (tedy vytvořit kaskádu, dokud to nespadne na 404)

Jak?

Nepsal bych, kdyby neprošel bezpočet článků, návodů a kde prostě na tohle řešení není…

Díky

Jan Suchánek
Člen | 404
+
0
-

@Montanus: Já jsem přesně tohle vyřešil vlastním routrem, který dostane fasádu, a v případě, že nemá url (slug) nakešovaný šáhne do repository a vytáhe id či přeloží id na url (slug), tzn. nepotřebuju tuhle kaskádu, není to lepší?

Editoval jenicek (29. 11. 2013 23:07)

Montanus
Člen | 10
+
0
-

No ale proč by nette na tohle nemyslel už v základu? Dyť já tam nechci /produkt-neco/ nebo /123-ponozka/ apod, každý SEO optimalizátor chce url úplně základní bez balastu kolem.

Věřím, že to musí jít v základním režimu nette.

enumag
Člen | 2118
+
0
-

@Montanus: Protože je to holý nesmysl, respektive nechápeš jak funguje routování. Rozhodnout zda URL matchuje danou routu je úkol routy. Pokud matchuje, pošle se to presenteru a ten už si s tím nějak poradí (když to v DB nenajde tak obvykle vyhodí BadRequestException která probublá na ErrorPresenter). Zkusit jednu routu, poslat presenteru a když to nevyjde zak zkusit druhou a jiný presenter je nesmysl už kvůli rychlosti. Správné řešení (jak už zaznělo) je vlastní router který se podívá do db a vyhodnotí který presenter má danou url zpracovat. Ty routy které jsou v Nette předepsané slouží pouze pro speciální případy kdy už z URL adresy lze přesně poznat o co se jedná, což samozřejmě není případ těchto url.

Editoval enumag (29. 11. 2013 23:40)

Montanus
Člen | 10
+
0
-

@jenicek a jak to tedy máš ve svém řešení?

Jan Suchánek
Člen | 404
+
0
-

@Montanus: No jednoduše máš routu, která standardně filtruje /<presenter>/<action>/<id [0–9]>, teď pomiň zda ty masky mám špatně či dobře. Já fitruju pouze parametr presenter a pokud se mi nazev shoduje podle databaze s např. tvým požadavkem např. /alfa-romeo-156 tak v db mám přímo překlad, co je to sakra za presenter a mám vytaráno, stejně tak mohu pěkně přeložit i action + zachovávam si i možnost používat id, což je fajn ne? A všechno dobře kešuj, při změně invaliduj a bude to rychlé.

Lze takhle přeložit i Sign:in na /prihlasit-se nebo Sign:in lang ⇒ en na login nebo jiné divočárny.

Editoval jenicek (29. 11. 2013 23:49)

enumag
Člen | 2118
+
0
-

@Montanus: Honza Tvrdík měl na jedné posobotě přednášku na téma databázových routerů. Videozáznam k dispozici není, ale zde je router, který Honza během přednášky sepsal: https://github.com/…omRouter.php. Pozn. tohle nemá sloužit jako vzorové ideální řešení ale spíše nástřel. Nejsou tam ošetřené speciální případy, POST parametry etc.

Editoval enumag (29. 11. 2013 23:48)

Montanus
Člen | 10
+
0
-

@jenicek dík za tip

Jan Suchánek
Člen | 404
+
0
-

@enumag: Pěkně! Já to mám podobně, ale využil jsem částečně Hosiplanuv Filter Route, jen s tím rozdílem že jsem vše delegoval na Fasádu, která rozhodovala zda použije Cache či Db, ale nevím zda se nevykašlat přidávat Route přímo ten manager či to neřídit pomocí událostí.

Jan Suchánek
Člen | 404
+
0
-

@enumag: Ještě by mě zajímala jedna věc. Je to to takto rozumně?

/**
 * @return \Nette\Application\IRouter
 */
public function createRouter()
{
    $router = new RouteList();

    $backRoutes = new RouteList('Back');
    $backRoutes[] = new Route('admin/<presenter>[/<action>[/<id>]]', 'Default:default');

    $frontRoutes = new RouteList('Front');

    $route = new SeoRoute('[<lang [cs|en|de]{2} >/]<presenter>[/<action>][/<id [0-9]+>]', array(
        'presenter' => 'Default',
        'action' => 'default',
        'lang' => 'cs',
        'id' => NULL
    ));

    $route->addFilter($this->routerFacade, "presenter");

    $frontRoutes[] = $route;

    $route = new Route('[<lang [cs|en|de]{2} >/]<presenter>[/<action>][/<id [0-9]+>]', array(
        'presenter' => 'Default',
        'action' => 'default',
        'lang' => 'cs',
        'id' => NULL,
    ));
    $frontRoutes[] = $route;

    $router[] = $backRoutes;
    $router[] = $frontRoutes;

    return $router;
}

v konstruktoru předávám filtr, tak aby Router věděl o lang a zároveň aby mohl pracovat s action a id.
V případě použití třeba /<presenter>/<action>/<id> (/kosik/pridat/1). Proto nemohu použít uvedený.

Zároveň Router umožňuje opravovat špatně zadané adresy na správné, např. /en/kosik vrátí na /kosik a naopak, ale také umožňuje překlad z /cs/basket/add/1 změnit na /kosik/pridat/1, jinak o překladech je i pěkné video z PS 36 od Pandy.

Setr $route->addFilter($this->routerFacade, „presenter“); zkusím vyměnit za Kdyby\Event a nebo se spíš hodí Kdyby\AOP?

enumag
Člen | 2118
+
0
-

Setr $route->addFilter($this->routerFacade, „presenter“); zkusím vyměnit za Kdyby\Event a nebo se spíš hodí Kdyby\AOP?

Nevidím k tomu důvod, mám pocit že by se to tím zbytečně zkomplikovalo.

Nabízí se jen zjednodušení pomocí globálních filtrů v Nette 2.1. Díky tomu už bys nemusel Route dědit.

Video od Pandy je fajn, jen práci s databází tam má úplně špatně protože dědí Selection.

Editoval enumag (30. 11. 2013 10:28)

Jan Suchánek
Člen | 404
+
0
-

@enumag: Ok, u globálních filtrů jak k nim předat lang abych mohl filtrovat podle ní?

V tuto chvíli musim předávat závislost přez ten setr. a RouteListu přez konstruktor v neonu. Prostě mě nadchlo předat to nějakému tomu Lisenteru, tak jsem ho chtěl vyzkoušet a ať si s tim poradí, když už se používá i jinde, příliš náročné?

Když už jsme u toho, jakým způsobem na cache? Prostě mít ve fasádě pro zjištění url dva repositáře jeden na cache a druhý na db? Fasáda dělá ještě nějaké mirné korekce než data předává zpět Route.

Jan Suchánek
Člen | 404
+
0
-

@enumag: Předpokládám, že takhle by to mělo být ok?

$route = new Route('[<lang [cs|en|de]{2} >/]<slug>[/<action>][/<id [0-9]+>]', array(
		NULL => array(
			/* překládám slug na presenter */
			Route::FILTER_IN => callback($this->urlService, 'filterIn'),
			/* překládám na slug a odstraňuji presenter */
			Route::FILTER_OUT => callback($this->urlService, 'filterOut'),
		),

));

	/* tzn. deleguji vše na model, ten dostal závislosti autowiringem z DI kontejneru  */

Dají se definovat defaultní hodnoty? Nebo je musím pouze dodefinovat v modelu?

Editoval jenicek (3. 12. 2013 18:05)

enumag
Člen | 2118
+
0
-

Co já vím? Sám jsem ty globální filtry ještě nepotřeboval, jen vim že tam jsou. :-D Tak jak to máš mi to připadá OK, jestli to funguje správně je druhá věc.

Jan Suchánek
Člen | 404
+
0
-

@enumag: Jo maká to pěkně, jen místo presenter musím dát slug, tak abych dostal pomlčkovou variantu (a ne název presenteru) a v modelu nastavit výchozí parametry.

Edit: funguje to i s výchozími parametry libově:

$route = new Route('[<lang [cs|en|de]{2} >/]<slug>[/<action>][/<id [0-9]+>]', array(
    NULL => array(
        Route::FILTER_IN => callback($this->urlService, 'filterIn'),
        Route::FILTER_OUT => callback($this->urlService, 'filterOut'),
    ),
    'presenter' => 'Default',
    'action' => 'default',
    'lang' => 'cs',
    'id' => NULL
));
$frontRoutes[] = $route;

Editoval jenicek (3. 12. 2013 18:07)