změna typu view

jfojtl
Člen | 10
+
0
-

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)

Honza Marek
Člen | 1664
+
0
-

Můžeš si napsat router, ve kterém bude proměnná $viewType.

jfojtl
Člen | 10
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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.

jfojtl
Člen | 10
+
0
-

No a kde se v tom případě řeší, jestli je požadavek AJAXový? Pokud používám snippety, tak to funguje automaticky díky routeru, ne?

Jakub Lédl
Člen | 55
+
0
-

A co takhle:

  1. 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í.

  1. Š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

  1. 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
+
0
-

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
+
0
-

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)

jfojtl
Člen | 10
+
0
-

Tohle už vypadá dobře, díky moc za nakopnutí :)

David Grudl
founder | 8310
+
0
-

Není nejjednodušší v té metodě render napsat něco jako $this->view .= ".$format" ?

jfojtl
Člen | 10
+
0
-

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_
Člen | 679
+
0
-

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
+
0
-

No ale to z toho budu mít metodu podobnou, jako jsem psal úplně v prvním příspěvku, ne?

_Martin_
Člen | 679
+
0
-

To si nemyslím. Záleží na tom, kde potřebuješ větvení rozlišovat. Pokud ti stačí výběr šablony, tak do BasePresenteru dáš

public function beforeRender()
{
	$this->view .= ".{$this->format}";
}

a je hotovo.

jfojtl
Člen | 10
+
0
-
<?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
founder | 8310
+
0
-

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.

jfojtl
Člen | 10
+
0
-

No, právě kvůli těmto důvodům jsem taky opustil od myšlenek zde. Ale pořád se mi to zdá jako porušování DRY principu. Na druhou stranu na nic čistšího jsem nepřišel.

RSS tématu Téma zavřeno