Routování, presenter, zanořené url – způsoby použití

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

Ahoj, rád bych se zeptal na pár drobností.

Pokud máme klasický sandbox a v něm budu vytvářet nějaký menší defakto statický webík, s tím, že tam bude pár dynamických sekcí (blog, galerie …)

routování

domena/blog
	- router: $frontRouter[] = new Route('<presenter>/<action>[/<id>]', 'Page:default'); // i kdyz se vola Blog:Default, tak tohle pravidlo to zachytí
	- presenter: renderDefault

domena/blog/php
	- router: $frontRouter[] = new Route('blog/<category>', 'Blog:category');
	- presenter: renderCategory($category)

domena/blog/php/co-je-to-include
	- router: $frontRouter[] = new Route('blog/<category>/<slug>', 'Blog:detail');
	- presenter: renderDetail($category, $slug)

konkrétní metody:

<?


	public function renderDefault($category = NULL)
	{


		$rows = $this->model->findAll();
		if ($category !== NULL)
		{
			$rows->where("clanky_kategorie.seo_nazev", $category);
		}

		$vp = $this['paginator'];
		$paginator = $vp->paginator;
		$paginator->itemsPerPage = 6;
		$paginator->itemCount = $rows->count("*");


		$selection = $this->model->findAll();
		if ($category !== NULL)
		{
			$selection->where("clanky_kategorie.seo_nazev", $category);
		}

		$selection->order("vydano DESC")
				  ->limit($paginator->itemsPerPage, $paginator->offset);
		$this->template->posts = $selection;



	}


	public function renderCategory($category)
	{
		$this->view = 'default';
		$this->renderDefault($category);
	}



	public function renderDetail($category, $slug)
	{
		$this->template->post = $this->model->findBySlug($category, $slug);

		$this->template->registerHelper('jush', function ($text) {

			$text = preg_replace_callback("~\[code(.*?)\]\s*(.*?)\s\[\/code\]~s", array($this, "printCode2"), $text);


			return $text;
		});

	}

Kdybych se například rozhodl mít články pod revizí

domena/blog/php/co-je-to-include/1
domena/blog/php/co-je-to-include/2
domena/blog/php/co-je-to-include/x

Tak bych postupoval takto:

	- router: $frontRouter[] = new Route('blog/<category>/<slug>/<revision>', 'Blog:revision');
	- presenter: renderRevision($category, $slug, $revision)

A ted bych musel zduplikovat obsah metody renderDetail

	public function renderRevision($category, $date, $slug, $revision)
	{
		$this->template->post = $this->model->findBySlug($date, $slug)->where("revision", $revision);

		$this->template->registerHelper('jush', function ($text) {

			$text = preg_replace_callback("~\[code(.*?)\]\s*(.*?)\s\[\/code\]~s", array($this, "printCode2"), $text);


			return $text;
		});

	}
  1. A ted mi jde tedy o to, jestli je na tomto způsobu něco špatného / zbytečného, nebo jak se to dělá v praxi.
  2. V případě nějakého zanoření URL jestli takhle ručně doplňovat do routeru způsoby url a jeho odchytávání na X způsobu
  3. A co taky s metodami, kdy renderCategory, renderDetail a renderRevision jsou skoro stejné, liší se jen v rozšířeném parametru do modelu ( a taky samozřejmě renderCategory má jinou šablonu než renderDetail a renderRevision) – jak to lépe zapsat.

Díky

Editoval Kcko (2. 8. 2014 10:11)

Jan Tvrdík
Nette guru | 2595
+
0
-

@Kcko Vezmu to pěkně postupně v bodech:

  1. Routy je potřeba mít vždy seřazené od konkrétních k obecným. Takže např. super-obecná routa <presenter>/<action>[/<id>] by měla být zřejmě úplně poslední.
  2. Pokud bys chtěl, aby ti ta super-obecná routa pracovala pouze z presenterem Page, tak ji musíš zkonkrétnit. Pak zároveň nemusí být poslední.
    new Route('page/<action>[/<id>]', 'Page:default');
  3. U routy pro Blog:revision jsem nepochopil, proč navíc ještě pracuješ s parametrem date, když revize je určená jednoznačně slugem a číslem revize, nebo ne? Dalo by se to pak spojit s Blog:detail:
    new Route('blog/<category>/<slug>[/<revision>]', 'Blog:detail');
    
    	public function renderDetail($category, $slug, $revision = NULL)
    {
    	$post = $this->model->findBySlug($category, $slug);
    	if ($revision) $post = $post->where("revision", $revision);
    	$this->template->post = $post;
    	// ...
    }
  4. Registraci helperů je většinou zvykem dávat do beforeRender nebo do createTemplate.
Kcko
Člen | 469
+
0
-

1 + 2) Můj router vypadá takhle momentálně

		$router = new RouteList();


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

		$router[] = $frontRouter = new RouteList('Front');

		$frontRouter[] = new Route('sluzby/<slug>', 'Page:sluzbyDetail');
		$frontRouter[] = new Route('blog', 'Blog:default');
		$frontRouter[] = new Route('blog/<category>', 'Blog:category');
		$frontRouter[] = new Route('blog/<category>/<date>-<slug>', 'Blog:detail');
		$frontRouter[] = new Route('reference', 'Reference:default');
		$frontRouter[] = new Route('reference/<slug>', 'Reference:detail');
		$frontRouter[] = new Route('kontakt', 'Kontakt:default');
		$frontRouter[] = new Route('znalosti', 'Page:znalosti');
		$frontRouter[] = new Route('sluzby', 'Page:sluzby');
		//$frontRouter[] = new Route('image-<id>', 'ImageResize:default');
		$frontRouter[] = new Route('<presenter>/<action>[/<id>]', 'Page:default');
  1. To $date tam nepatří, špatně jsem to zkopíroval, je to ze starého systému kvůli zachování URL, článek se mi identifikuje nejen podle slugu ale i podle datumu.
  2. taky vím, potřeboval jsem to pouze v jedné metodě, tak jsem to šoupnul rovnou do ní.

Vlastně to co řeším je, to čím jsi mi asi odpověděl v #3.

Abych mohl vícero podobných „akcí“ (detail článku / detail článku s revizí) obsloužit jedním renderem (tj. předat si více parametetrů).

Tam kde by to bylo složité nebo proti logice, tj. detail článku X výpis článků podle kategorie nebo všechny tam se použije už jiný render.

V tom případě se ještě zeptám takto.

Je něco špatné pokud je

domena/blog
domena/blog/kategorie

když použiji, nebo to napsat jinak?

public function renderCategory($category)
{
    $this->view = 'default';
    $this->renderDefault($category);
}

Díky.

Jan Tvrdík
Nette guru | 2595
+
0
-

@Kcko Volat z jedné render metody jinou je většinou blbost. Pokud má renderDefault a renderCategory dělat to samé, tak ti stačí (logicky) jen jedna z těch akcí. Většinou lze použít i jednu routu:

$frontRouter[] = new Route('blog[/<category>]', 'Blog:default');

public function renderDefault($category = NULL)
{
	// ...
}
Kcko
Člen | 469
+
0
-

@JanTvrdík Jasně, chápu.

1)

Šlo by pak v hypotetickém příkladě:

domena/blog/php  // kategorie
domena/blog/co-to-je-echo // detail clanku

rozpoznat jakou metodu vykreslit?

(Já osobně bych to tedy řešil 2 způsoby, který je vhodnější?)

- domena/blog/php // kategorie, nechame
a) domena/blog/php/co-je-to-echo // apendovanim za kategorii
b) domena/clanek/co-je-to-echo // nebo uplne jinou zmenou

A ještě by mě zajímala jedna věc. Vypisuji reference. Nad nimi je menu, přes něj lze filtrovat (eshopy / intranety / sportovní systémy atd.)

A teď řešit to přes handle nebo tak jak to řeším teď (parametr v akci), s tím, že URL je tedy poté

domena/reference // bez filtru
domena/reference?flag=eshop // s vybranym filtrem, URL ve tvaru domena/reference/eshop už nereším, protože by se to mlátilo s detailem reference.
	public function renderDefault($flag = NULL)
	{

		$rows = $this->model->findAll();
		if ($flag !== NULL)
		{
			$rows->where("typ", $flag);
		}

		$vp = $this['paginator'];
		$paginator = $vp->paginator;
		$paginator->itemsPerPage = 9;
		$paginator->itemCount = $rows->count("*");

		$selection = $this->model->findAll();
		if ($flag !== NULL)
		{
			$selection->where("typ", $flag);
		}

		$selection->order("datum_realizace DESC");
		$selection->limit($paginator->itemsPerPage, $paginator->offset);
		$this->template->data = $selection;



	}

Už vše … :)

Editoval Kcko (2. 8. 2014 14:55)

Jan Tvrdík
Nette guru | 2595
+
0
-

Šlo by pak v hypotetickém příkladě:

domena/blog/php  // kategorie
domena/blog/co-to-je-echo // detail clanku

rozpoznat jakou metodu vykreslit?

@Kcko Lze to rozeznat i bez změny struktury URL. Pamatuj, že cokoliv, co dává logicky smysl, lze taky naroutovat =)

$router[] = new Route('blog[/<category>]', [
	'presenter' => 'Blog',
	'action' => 'default',
	NULL => [
		Route::FILTER_IN => function (array $params) use ($model) {
			if (isset($params['category']) && !$model->categoryExist($params['category'])) {
				return NULL; // do not accept
			}
			return $params;
		},
		Route::FILTER_OUT => function (array $params) use ($model) {
			if (isset($params['category']) && !$model->categoryExist($params['category'])) {
				return NULL; // do not accept
			}
			return $params;
		}
	]
]);

$router[] = new Route('blog/<slug>', 'Blog:detail');

V praxi bych ale stejně použil radši úpravu struktury URL (např. domena/clanek/co-je-to-echo). Takhle bys musel řešit, aby slugy kategorií a článků nikdy nekolidovali.

řešit to přes handle nebo tak jak to řeším teď (parametr v akci)

Nevím, jak bys to dělal přes handle, ale tohle vypadá jako normální, „v pohodě“ řešení.

Kcko
Člen | 469
+
0
-

@JanTvrdík

Lze to rozeznat i bez změny struktury URL. Pamatuj, že cokoliv, co dává logicky smysl, lze taky naroutovat =)

Hezký příklad, děkuji :-)

V praxi bych ale stejně použil radši úpravu struktury URL (např. domena/clanek/co-je-to-echo). Takhle bys musel řešit, aby slugy kategorií a článků nikdy nekolidovali.

Ano, to je pravda, proto bych stejně asi použil kompletní url, aby člověk i viděl kam je to zařazeno a mohl třeba odmazávat a tak.

Nevím, jak bys to dělal přes handle, ale tohle vypadá jako normální, „v pohodě“ řešení.
Vlastně jsem v totálních začátcích řešil jak si předat hodnotu z komponenty do presenteru.
Měl jsem články (presenter), vedle toho kalendář s datumy (komponenta) a potřeboval jsem vybírat články dle vybraného datumu. Takže jsem vykonal handle akci z formuláře s nějakým datumem a ten jsem si v akci presenteru články uložil a v renderu jsem to dle toho vyfiltroval. To je asi v pořádku ne?

Nicméně si budu muset ještě jednou znovu projít na co ty handly jsou vlastně nejlepší :-)

Tak díky za pomoc.