změna typu view

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
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
Nette Core | 8228
+
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_
Generous Backer | 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_
Generous Backer | 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
Nette Core | 8228
+
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.