Router vracia objekty / entity namiesto skalarov
- David Ďurika
- Člen | 328
Pouziva niekto router tak ze mu po spracovani url vitiahne uz aj spravne netity a nie len IDcka ?
napr url: „/liptov/chaty“ router spracuje a vrati parametre kde budu dve entity, jedna pre „liprov“ a druha pre „chaty“, a nevrati len obycajne idcka tych entity.
potom si v presenteroch nemusim vytahovat entity (ani injectovat zbytocne repositare) lebo parametre uz su entity:
<?php
class FooPresenter extends BasePresenter
{
public function actionBar($region, $typZariadenie)
{
// $region instanceof \Entity\Region
// $typZariadenie instanceof \Entity\RentalType
}
}
?>
Editoval achtan (8. 2. 2013 8:32)
- frosty22
- Člen | 373
Vypadá to zajímavě, leč možná bych viděl problém ve dvou věcech:
- Zbytečné query navíc? Nemusíš vždy potřebovat ony entity parametrů, viz příklad kdy například potom ihned přesměrováváš jinam.
- Typová kontrola – dokázal bych si představit, že budu chtít provádět typovou kontrolu, avšak co se stane když bude invalidní ID parametru? Potom nebude nalezena entita, a typová kontrola skončí chybou „Fatal Error: Argument 1 must be an instance of \Entity\Region“, ikdyž asi by se holt musel parametr vždy uvádět jako nepovinný
- To už nevidím jako problém rozhraní, ale spíše implementace a to, jak vybírat které parametry převádět na entity a které ne? Toto by se asi nemohlo plně automatizovat.
- enumag
- Člen | 2118
@frosty22:
- Neumím si dost dobře přestavit situaci kdy bych přesměrovával aniž bych se na tu entitu vůbec podíval. Přesměrování obvykle závisí na nějaké podmínce.
- Řešením je, že router danou URL adresu jednoduše odmítne. Pak máš parametry povinné a klidně i s typovou kontrolou.
- Dá, ne sice úplně ale to bych stejně považoval za nežádoucí.
- Felix
- Nette Core | 1196
- frosty22
- Člen | 373
Přišel již někdo na nějaké efektivní řešení? Taky jsem si s tím trošku hrál, ale bohužel jsem pohořel. Nechtěl jsem upravovat nette, takže jsem šel cestou, kdy ve startupu jsem si zavolal svůj objekt, kterému jsem předal presenter, ten zjistil jaká action a render metoda se volá a přes reflection jsem si přečetl typehinting těchto metod a získal si patřičnou entitu dle primárního klíče.
Následně jsem přehodil parametry presenteru, sice tohle funguje, avšak následně to končí problémem, kdy si nette sahá pro parametry, aby zpětně vytvářelo request, a objekty se mu nelíbí.
Napadlo mě ještě tedy řešení, kdy přepíši metodu run(), která by toto řešila a pouze zavolala action a render metody s entitami a neměnila parametry presenteru. Což je jistě bezpečenější řešení než hledat, kde všude nette parametry presenteru potřebuje. Jen se mi moc nelíbí, že run() je trošku delší kus kódu, který bych musel v podstatě copy&paste a jen doplnit volání s entity.
- frosty22
- Člen | 373
Heuréka, tak již jsem našel snad řešení – zatím tedy BETA verze :) Ještě jsem to pořádně neotestoval a možná by se hodilo to cachovat, ale funguje a zase tak extrémní prasárna to není .. do BasePresenteru stačí tedy přidat (a přepsat tím metodu tryCall):
<?php
/**
* @var \Doctrine\ORM\EntityManager
*/
protected $entityManager;
/**
* Calls public method if exists.
* @param string
* @param array
* @return bool does method exist?
*/
protected function tryCall($method, array $params)
{
$rc = $this->getReflection();
if ($rc->hasMethod($method)) {
$rm = $rc->getMethod($method);
if ($rm->isPublic() && !$rm->isAbstract() && !$rm->isStatic()) {
$this->checkRequirements($rm);
$args = $rc->combineArgs($rm, $params);
if (\Nette\Utils\Strings::match($method, "~^(action|render|handle).+~")) {
$methodParams = $rm->getParameters();
foreach ($methodParams as $i => $param) {
/** @var \Nette\Reflection\Parameter $param */
if ($className = $param->getClassName()) {
$paramName = $param->getName();
if ($paramValue = $args[$i]) {
$entity = $this->findById($className, $paramValue);
if ($entity) {
$args[$i] = $entity;
} else {
throw new \Nette\Application\BadRequestException("Value '$paramValue' not found in collection '$className'.");
}
} else {
if (!$param->allowsNull()) {
throw new \Nette\Application\BadRequestException("Value '$param' cannot be NULL.");
}
}
}
}
}
$rm->invokeArgs($this, $args);
return TRUE;
}
}
return FALSE;
}
/**
* Find entity by ID.
* @param string $entityName
* @param int $id
* @return object|null
*/
protected function findById($entityName, $id)
{
return $this->entityManager->find($entityName, $id);
}
?>
A poté pokud chcete v presenterech přijímat entitu namísto skaláru, stačí jen definovat typovou kontrolu (umí samozřejmě i NULL):
<?php
class TestPresenter extends \App\BasePresenter
{
public function actionDefault(\Entities\User $foo, \Entities\Shop $shop = NULL)
{
...
}
public function renderDefault(\Entities\User $foo)
{
...
}
}
?>
Mělo by to předávat tedy jak render*, action* i handle* metodám v případě, že definujete typovou kontrolu. A v případě nenalezené entity, tak vyhodí BadRequestException případně pokud se předá prázná hodnota a je povolený NULL (viz výše argument $shop), tak to projde.
- David Ďurika
- Člen | 328
zaujimave, ja to sice robim tam ze us samotny router vracia a prijima entity, ale ani toto nieje zle…
- Tomáš Votruba
- Moderator | 1114
Výše uvedený přístup jsem implementoval do balíčku Zenify/DoctrineMethodsHydrator.