Routování, presenter, zanořené url – způsoby použití
- Kcko
- Člen | 469
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;
});
}
- 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.
- 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
- 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
@Kcko Vezmu to pěkně postupně v bodech:
- 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í. - 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');
- U routy pro
Blog:revision
jsem nepochopil, proč navíc ještě pracuješ s parametremdate
, když revize je určená jednoznačně slugem a číslem revize, nebo ne? Dalo by se to pak spojit sBlog: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; // ... }
- Registraci helperů je většinou zvykem dávat do
beforeRender
nebo docreateTemplate
.
- Kcko
- Člen | 469
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');
- 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.
- 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
@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
@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
Š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
@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.