změna typu view
- jfojtl
- Člen | 10
Ahoj, začínám s Nette a mám otázečku. Co když chci udělat web,
který nabídne API ve verzích JSON i XML. Dejme tomu, že mám teď
loginPresenter
, který se pouze vykresluje podle phtml view. Tak
musím udělat z loginPresenter
něco jako
baseLoginPresenter
, kde si naprogramuju získávání dat, pro
jednotlivá API definovat třeba
xmlLoginPresenter extends baseLoginPresenter
(jsonLoginPresenter extends baseLoginPresenter
), v nich vytvořit
render metody a napsat si router, kterej podle typu požadavku vytvoří buď
jsonLoginPresenter
nebo xmlLoginPresenter
?
Doufám, že jsem to popsal srozumitelně. A moje otázka zní, existuje jednoduší způsob? Asi bych se bránil něčemu jako:
<?php
public function renderLoginForm() {
switch($viewType) {
case 'xml':
$this->setView(...xmlLogin.phtml);
break;
case 'json':
$this->setView(...json.phtml);
break;
case '...':
...
break;
}
}
?>
Editoval jfojtl (21. 1. 2010 23:44)
- jfojtl
- Člen | 10
Ten příklad s $viewType, se kterou pracuje render funkce v jednom presenteru byl takhle napsán jen jako modelový příklad. Sám bych nechtěl, aby to tak fungovalo. Nejčistší cestu, kterou vidím je napsat si router, který právě ten $viewType pozná, ale podle toho zavolá renderovací metodu v presenteru $viewType.„presenterPresenter“. Ale neumí tohle už nějak framework? Nebo neřešil tohle již někdo? Jak jste to řešili? Možná jsem něco přehlédl, ale bohužel jsem o ničem takovém nenašel zmínku :(
- Jakub Lédl
- Člen | 55
Asi nejkorektnější variantou (podle mě) by bylo použít metodu
presenteru terminate()
, která může přijímat parametr
$response. Tento parametr musí být implementace IPresenterResponse. V Nette
už jeden, který bys mohl využít je: JsonResponse. Prostě v presenteru
zavoláš:
<?php
...
$this->terminate(new JsonResponse($data_co_chci_poslat));
...
?>
A JsonResponse ta data vezme, zakóduje přes json_encode(), nastaví Content-Type na ‚application/json‘ (dá se nastavit i jiný) a pošle na výstup.
Pro XML odpovědi by bylo řešení napsat si vlastní XmlResponse a
v implementované metodě IPresenterResponse::send() předaná data
serializovat, nastavit Content-Type a klasicky přes echo
poslat
prohlížeči.
Tento způsob se mi líbí už z ASP.NET MVC, kde Controller obsahuje
metody, které vrací instance potřebných odpovědí, a pak v controlleru
volám jenom return View("ViewName");
nebo
return Json(data);
etc.
Editoval Jakub Lédl (22. 1. 2010 0:02)
- jfojtl
- Člen | 10
Jenže na tomto řešení se mi nelíbí, že tu logiku, které view vyhodit budu obsluhovat až v render metodě presenteru. Co když budu chtít zakázat XML verzi API? Tak budu prolízat všechny presentery? Mě se prostě jako nejčistší řešení jeví tohle řešit už v routeru. Router by měl už zavolat presenter, který se chová jak má jen pro svoje pohledy. Ale nevím, jestli je tohle řešení správné a zda nenarazím na nějaká úskalí. Proti mluví to, že presentery by se hodně podobali a tak bych se asi opakoval. Ale s tím switchem se budu opakovat v každé metodě taky.
- Honza Marek
- Člen | 1664
Router by se měl starat jen o převod adres na dotaz na presenter a naopak. Nějaký oprávnění dělat by se mělo řešit až v presenteru.
Ještě k postu Jakuba bych doporučil jako $data_co_chci_poslat objekt $this->payload v presenteru. Je na to určen.
- Jakub Lédl
- Člen | 55
A co takhle:
- Při routingu si nastavím, že chci pouze ten a ten formát:
<format xml|json|whatever>
, jiný než tyto tři to
nepustí.
- Šablony si nastavím tak (např.) že budu mít šablony pro jednotlivé formáty v různých složkách, např:
/templates
`--/Default
`--/json
| `--default.phtml
`--/xml
`--default.phtml
- V presenteru přepíšu metodu formatTemplateFiles tak, aby vracela korektní cestu, včetně různých složek pro jednotlivé formáty.
EDIT:
$this->payload
Himlhergot, už bych měl fakt jít spát :-)
Editoval Jakub Lédl (22. 1. 2010 0:33)
- jfojtl
- Člen | 10
Jakub Lédl napsal(a)
No, to už by bylo konkrétní řešení v tom routeru. I když nerozumím tomu řádku
<format xml|json|whatever>
Ale obecně, pořád se nemůžu rozhodnout, kam s touhle logikou. Jestli do routeru nebo do renderovací metody nebo ještě další cesta, která mě napadlo podle příspěvku nahoře, napsat si basePresenter, kterej by nastavil cestu k view adresáři podle požadavku.
- Jakub Lédl
- Člen | 55
Ten řádek <format xml|json|whatever>
znamená, že routa
povolí pro parametr <format> pouze hodnoty xml, json, nebo whatever, a
pokud je tam hodnota jiná, routa nevyhoví požadavku.
Ta logika by přišla do BasePresenteru. Třída Presenter má metodu formatTemplateFiles, která vrací pole řetězců, které se mají použít jako cesta k souborům s šablonami. Tato metoda by se musela překrýt tak, aby vracela cestu i s těmi podsložkami, které by dělily šablony podle formátů.
EDIT:
Trochu jsem si s tím pohrál, tohle mi vyšlo (pokud najdete nějakou krpu, vězte, že touhle dobou už normálně ležím v posteli, ale). Je to víceméně originální Davidův kód, pouze jsem přidal pár řádků:
<?php
abstract class BasePresenter extends Presenter {
public function formatTemplateFiles($presenter, $view) {
$frmt = $this->getParam('format');
$appDir = Environment::getVariable('appDir');
$path = '/' . str_replace(':', 'Module/', $presenter);
$pathP = substr_replace($path, '/templates', strrpos($path, '/'), 0);
$path = substr_replace($path, '/templates', strrpos($path, '/'));
return ($frmt) ? array(
"$appDir$pathP/$frmt/$view.phtml",
"$appDir$pathP/$view.$frmt.phtml",
"$appDir$pathP.$view.$frmt.phtml",
"$appDir$path/@global.$view.$frmt.phtml"
) : array(
"$appDir$pathP/$view.phtml",
"$appDir$pathP.$view.phtml",
"$appDir$path/@global.$view.phtml"
);
}
}
?>
Soubor se šablonou se uloží buď jako
Presenter/format/action.phtml
,
Presenter/action.format.phtml
(Rails-style) nebo jako
Presenter.action.format.phtml
.
Editoval Jakub Lédl (22. 1. 2010 1:17)
- David Grudl
- Nette Core | 8228
Není nejjednodušší v té metodě render napsat něco jako
$this->view .= ".$format"
?
- jfojtl
- Člen | 10
Jenže bude to opravdu jednoduší? Budu muset přepsat všechny render metody. A když se rozhodnu dopsat další formát výstupu, tak budu přepisovat zase všechny presentery? A co když budu chtít nějaký formát zakázat? Mám všechnu tuhle logiku napsat do render metody? Mě se tohle řešení nezdá moc koncepční :(.
- _Martin_
- Generous Backer | 679
jfojtl napsal(a):
Jenže bude to opravdu jednoduší? Budu muset přepsat všechny render metody. A když se rozhodnu dopsat další formát výstupu, tak budu přepisovat zase všechny presentery? A co když budu chtít nějaký formát zakázat? Mám všechnu tuhle logiku napsat do render metody? Mě se tohle řešení nezdá moc koncepční :(.
Pokud reaguješ na to, co napsal David, tak tam není důvod nic přepisovat. Pokud budeš chtít něco zakázat, tak to prostě smažeš. A pokud budeš chtít něco přidat, napíšeš nové šablony.
- jfojtl
- Člen | 10
<?php
public function beforeRender()
{
$this->view .= ".{$this->format}";
}
?>
mi asi bohužel nebude stačit. A asi ani ty rady výše. To ještě nikdo nenabízel nějakou službu s API? Jak to řešíte? Presentery asi budou trošku odlišné a asi se vydám ještě tou cestou přepsaného routeru.
- David Grudl
- Nette Core | 8228
jfojtl napsal(a):
mi asi bohužel nebude stačit. A asi ani ty rady výše. To ještě nikdo nenabízel nějakou službu s API? Jak to řešíte? Presentery asi budou trošku odlišné a asi se vydám ještě tou cestou přepsaného routeru.
Jiný formát výstupu dost často znamená zcela odlišný presenter nebo i umístění v hierarchii presenterů. Především proto, že se vypisují surová data, XML nemá žádný layout, ověřování autentizace funguje malinko jinak. Takže na to používám obvykle speciální presentery.