Předávání objektu akcím presenteru a metodě link
- spidy
- Člen | 55
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í :).
- Filip Procházka
- Moderator | 4668
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.
- Jan Voráček
- Člen | 90
Ú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
@Jan Voráček: tou metodou je právě zmiňovaný
__toString()
jak píše gmvasek tady
- Jan Voráček
- Člen | 90
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
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.
- wdolek
- Člen | 331
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
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
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
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
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
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…
- Filip Procházka
- Moderator | 4668
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
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
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?
- Filip Procházka
- Moderator | 4668
@**wdolek**: Tady přichází k řeči to škaredé složité DI :) Viz stará verze mého routeru, jako demonstrace.