Předávání objektu akcím presenteru a metodě link

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

Bylo by super, kdyby se dalo předávat akcím presenteru a metodě link objekt (nejčastěji asi řádek z databáze). Představoval bych si to nějak takhle:

presenter

class ProductPresenter extends BasePresenter
{
	public function renderView(Product $product) { //nyní public function renderView($id, $name, $category) {
		$this->link('View', $product); //nyní $this->link('View', array('id' => $id, 'name' => $name, 'category' => $category));
	}
}

routy

$router = $container->router;
$router[] = new Route('<category>/<slug>-<id>', array(
	'presenter' => 'Product',
	'action' => 'view',
	'product' => array(
		Route::FILTER_IN => function(array $params) use ($container) { //popř. nahradit array $params za $category, $slug, $id
			return $container->productModel->findOneById($params['id']);
		},
		Route::FILTER_OUT => function(Product $product) use($container) {
			return array('category' => $product->category, 'slug' => $product->slug, 'id' => $product->id); //nebo return (array) $product;
		}
	)
));

Poslední dobou mám pocit, že je to jediná věc, která mi v Nette chybí :).

mkoubik
Člen | 728
+
0
-

Počkat, tohle nejde? Chtěl jsem podobným způsobem předělat svoje routy, hlavně kvůli šablonám (<a n:href="Articles:show $article"> místo <a n:href="Articles:show $article->id">). Musim to vyzkoušet…

spidy
Člen | 55
+
0
-

Pokud to funguje, tak dělám asi něco špatně, Nette se snaží převést objekt na string. Ale předpokládám, že kdyby to takhle fungovalo, tak by se to používalo v tutoriálech a kdyby to byla nějaká novinka, tak by snad byla na changelogu…

Editoval spidy (25. 9. 2011 19:28)

mkoubik
Člen | 728
+
0
-

Já nevim jestli to funguje nebo ne, ale přišlo by mi to užitečný. Takhle ten objekt načítám dvakrát – jednou v routě, abych zjistil jestli existuje, a pak v presenteru (samozřejmě ho repository hledá jenom jednou, ale stejně mi to smrdí).

Filip Procházka
Moderator | 4668
+
0
-

Parametry, které nejsou scalarni tak routa schválně zahazuje. Už jsme to tu kdysi dávno řešili a nic nevyřešili. David nám nechce prozradit, proč nechce, aby jsme předávali filtrům objekty.

Aurielle
Člen | 1281
+
0
-

Při linkování ze strany Presenteru to padá tady, tedy v metodě argsToParams. Pokud objekt nemá metodu __toString() vracící například slug pro routu, tak to spadne na Recoverable erroru…

Jan Voráček
Člen | 90
+
0
-

Úplně by stačilo, kdyby přibylo rozhraní pro „routovatelné entity“ s jednou metodou, která by vracela slug, a routa ho akceptovala :)

Patrik Votoček
Člen | 2221
+
0
-

@Jan Voráček: tou metodou je právě zmiňovaný __toString() jak píše gmvasek tady

Jan Voráček
Člen | 90
+
0
-

Vím, že s metodou __toString() to funguje, ale přijde mi zvláštní, aby textová reprezentace celého objektu byl slug. Navíc pokud člověk takový zdroják dostane do ruky, dozví se až z phpDocu (pokud ho vůbec někdo napsal), k čemu tam ta metoda je. Celé je to takové WTF :)

Ondřej Mirtes
Člen | 1536
+
0
-

Taky se přidávám s tímhle požadavkem. Nechci si kurvit objekty, které strkám do rout (třeba entity) nějakou metodou __toString(), která je vůbec nemá zjaímat.

spidy
Člen | 55
+
0
-

Pokud by se podle Davida měla na toto opravdu požívat metoda __toString(), tak by mě celkem zajímalo, jak to realizovat s Nette\Database…

Majkl578
Moderator | 1364
+
0
-

Bump, taky jsem pro.

wdolek
Člen | 331
+
0
-

Kdyz se nad tim zahloubam, v requestu mi vzdy tecou jen stringy a ne objekty. Nebude pak naopak zatizene WTF faktorem, ze ti do renderAction( ) / `actionRender( ) leze primo instance neceho?

A proc by mel byt router zavisly na tvych modelech (byt to tam nastrkas pres nejakou anonymni funkci)?

Filip Procházka
Moderator | 4668
+
0
-

Já si naopak říkám, proč by nemohl? Právě router rozhoduje o tom, jestli konkrétní cesta je ta správná a pokud kontroluješ existenci objektu až v presenteru, tak už to routeru nevrátíš.


Btw: Doctrine má tu výhodu, že když si v routeru potvrdím existenci nějakého objektu, tak si vrátím místo objektu jeho identifikátor. Díky IdentityMap a UnitOfWork se mi tak objekt načte jenom jednou, protože si řeknu přesně o něj.

$repo = $container->entityManager->getRepository('Node');
$router[] = new Route('/<node .*>', array(
	'nodeId' => array(
		'presenter' => 'Front',
		'action' => 'default',
		Route::FILTER_IN => function ($name) use ($repo) {
			$node = $repo->findByName($name);

			// když vrátí null, routa bude odmítnuta
			return $node ? $node->getId() : NULL;
		},
		Route::FILTER_OUT => function ($id) use ($repo) {
			return $repo->find($id)->getName();
		},
	)
));

A co s tím v presenteru?

public function actionDefault($nodeId)
{
	$node = $this->context->entityManager->getRepository('Node')->find($nodeId);
	// nevykonává se žádný SQL, protože IdentityMap už entitu s tímhle ID má načtenou
}

Takže co? Nepotřebuju předávat objekt, proč taky :)


Pro-Tip: Je taky možné si vytvořit nějaký Helper, na převádění vstupů na objekty (v BasePresenteru).

protected function entityFromParam($class, $paramName, $name = NULL)
{
	if (!isset($this->params[$paramName])) {
		return;
	}

	$repository = $this->context->entityManager->getRepository($class);

	$id = $this->params[$paramName];
	$entity = $repository->find($id);

	$this->params[lcFirst($name ?: $class)] = $entity;
}

Nyní můžu upravit svůj presenter takto:

protected function startup()
{
	$this->entityFromParam('Article', 'articleId');
	$this->entityFromParam('Poll', 'pollId');

	parent::startup();
}

public function actionDefault(Article $article)
{
	// nejsem si jistý tím type-hintem, ale nevím o ničem, co by tomu bránilo.
}

Jedna nevýhoda tu je, musí se tomu přizpůsobit generování odkazů

{link this, 10} <!-- nestačí -->
{link this, 'articleId' => 10}
wdolek
Člen | 331
+
0
-

Dobre, ale co kdyz:

Pripad 1
Je pozadovan clanek s neexistujicim ID… Ty se ho uz v routeru snazis najit – clanek neexistuje – co ted? Ma se router postarat o presmerovani nekam jinam, nebo zobrazeni nejake chybove hlasky? (To ale preci neni prace routeru)

Pripad 2
V administracnim rozhrani chce uzivatel zobrazit obsah stranky, ke ktere nema prava. Ma se router zajimat o prava, nebo z databaze pracne vytahnout data a sestavit objekt, aby to cele bylo zahy v presenteru zahozeno kvuli chybijicim pravum?

Toto reseni je tedy libive, ale dle meho nazoru patricne neuniverzalni… nebo ne? :)

Aurielle
Člen | 1281
+
0
-

Případ 1: Router vrátí NULL, Nette tedy použije nejvíce obecnou routu (nebo samo Nette hned vrátí BadRequestException, neexistuje-li daný presenter – pokud jsou routy nastaveny tak, že by třeba node odpovídal v obecné routě presenteru). V presenteru se při dotazu na konkrétní objekt vrátí také NULL, protože ID buď neexistuje nebo nemůže být string. A tam se už dá vyhodit BadRequestException.

Filip Procházka
Moderator | 4668
+
0
-

wdolek napsal(a):

Pripad 1
Je pozadovan clanek s neexistujicim ID… Ty se ho uz v routeru snazis najit – clanek neexistuje – co ted? Ma se router postarat o presmerovani nekam jinam, nebo zobrazeni nejake chybove hlasky? (To ale preci neni prace routeru)

Moje řešení s tímhle schválně nepočítá. Pokud chceš, aby neexistující články zpracovával konkrétní presenter, místo NULL vrátíš třeba 0, to je fuk, potom si to v presenteru ošetříš.

Pripad 2
V administracnim rozhrani chce uzivatel zobrazit obsah stranky, ke ktere nema prava. Ma se router zajimat o prava, nebo z databaze pracne vytahnout data a sestavit objekt, aby to cele bylo zahy v presenteru zahozeno kvůli chybějícím právům?

V tomto případě bych práva kontroloval v presenteru a patřičně ošetřil, ať už vyhozením výjimky a zpracováním ErrorPresenterem, nebo přesměrováním na „chybový pohled“.

Toto reseni je tedy libive, ale dle meho nazoru patricne neuniverzalni… nebo ne? :)

Dovolím si tvrdit, že moc obecná řešení nemůžou být obecně dobrá :) (ve většině případů) Ale svůj účetl to plní, ne? Ušetřil jsem výkon a pracuji s objekty.

spidy
Člen | 55
+
0
-

HosipLan napsal(a):
Dovolím si tvrdit, že moc obecná řešení nemůžou být obecně dobrá :) (ve většině případů) Ale svůj účetl to plní, ne? Ušetřil jsem výkon a pracuji s objekty.

Pořád mám pocit, že je to zbytečně složité… Navíc třeba s Nette\Database něčeho takového nedocílíš, což mi přijde zvláštní, vzhledem k tomu že to je nativní knihovna v Nette…

wdolek
Člen | 331
+
0
-

Spise jsem narazel na to, ze to neni vubec „lazy“ – pomerne dost aplikacni logiky resim uz v routeru, kam reseni podobnych veci proste nepatri (z filozofickeho hlediska).

Filip Procházka
Moderator | 4668
+
0
-

wdolek napsal(a):

Spise jsem narazel na to, ze to neni vubec „lazy“ – pomerne dost aplikacni logiky resim uz v routeru, kam reseni podobnych veci proste nepatri (z filozofickeho hlediska).

Chápu o co ti jde, ale dalo by se o tom dlouze hádat, protože v routeru nic neřeším, ale předávám tam objekt, který se o to postará. Vždyť to jsou callbacky, v routeru se jenom zavolají :)

No a taky, proč by to mělo být lazy? Můžu to přece cachovat. Nette má moc krásně řešenou invalidaci.

spidy napsal(a):

Navíc třeba s Nette\Database něčeho takového nedocílíš, což mi přijde zvláštní, vzhledem k tomu že to je nativní knihovna v Nette…

Jakto, že nedocílím? Stačí si udělat primitivní IdentityMapu, třeba klidně v repozitáři a funguje mi to úplně stejně. Znáš DI, ne? :)

PS: Neobhajuju nemožnost pracovat s objekty, nebo naopak. Jenom nabízím řešení.

spidy
Člen | 55
+
0
-

HosipLan napsal(a):
Jakto, že nedocílím? Stačí si udělat primitivní IdentityMapu, třeba klidně v repozitáři a funguje mi to úplně stejně. Znáš DI, ne? :)

PS: Neobhajuju nemožnost pracovat s objekty, nebo naopak. Jenom nabízím řešení.

A zase je to práce navíc. Na Nette\Database a NotORM se mi líbí, že není potřeba prakticky nic dělat a můžu to rovnou použít… Myslím si, že kdyby se dalo pracovat s objekty, bylo by to jednodušší, čistší a hezčí :)

wdolek
Člen | 331
+
0
-

HosipLan napsal(a):

Chápu o co ti jde, ale dalo by se o tom dlouze hádat, protože v routeru nic neřeším, ale předávám tam objekt, který se o to postará. Vždyť to jsou callbacky, v routeru se jenom zavolají :)

Take chapu, jak to myslis :D problem je v tom, ze pravdepodobne ty callbacky nastavis nekde v bootstrapu, ne? Neumim si ted predstavit, jak zaridit, aby presenter sam rekl routeru, co mu ma predat – protoze v momente, kdy je ve hre samotny presenter je vlastne uz pozde ovlivnit chovani routeru (a dokonce je to i zbytecne).

Taktez reseni, ze by ten callback byla treba staticka funkce presenteru, kterou bys jen v callbacku nastavil, moc hezke reseni (byt funkcni) – jak si na tu statickou tridu napises unit test?

mkoubik
Člen | 728
+
0
-

No já mám normálně router(y) jako službu(y) a předávám mu service jako závislost, zatím jsem nenarazil na nic, co by musel řešit presenter.

Filip Procházka
Moderator | 4668
+
0
-

@**wdolek**: Tady přichází k řeči to škaredé složité DI :) Viz stará verze mého routeru, jako demonstrace.

wdolek
Člen | 331
+
0
-

:) no mate pravdu, ze DI a sluzby jsem v souvislosti s Routerem vubec nepredpokladal.