DI v komponentách (formuláře)
- darthcz
- Člen | 113
Zdravím,
chci se zeptat na optimální postup při předávání závislostí komponentám, které jsou vytvářené v továrničkách presenteru. Pochopil jsem, že nově se v presenterech používají inject metody. Dá se používat předávání závislostí definovaných v configu i komponentám?
Mám zhruba následující kód (zkráceno):
namespace MTS\App\AclModule\Presenters;
class AclPresenter extends BasePresenter
{
private $aclFacade;
public function injectAclFacade(\MTS\App\AclModule\Models\Facades\AclFacade $aclFacade)
{
if ($this->aclFacade !== NULL)
throw new \Nette\InvalidStateException('AclFacade has already been set.');
$this->aclFacade = $aclFacade;
}
protected function createComponentStoreAclRuleForm()
{
$form = new \MTS\App\AclModule\Components\Forms\AclRuleForm();
$form->injectAclFacade($this->aclFacade);
$form->create();
$form->onSuccess[] = callback($form, 'storeAclRuleFormSubmitted');
return $form;
}
}
namespace MTS\App\AclModule\Components\Forms;
use Nette\Application\UI\Form;
class AclRuleForm extends \Nette\Application\UI\Form
{
private $aclFacade;
public function __construct(\Nette\ComponentModel\IContainer $parent = NULL, $name = NULL)
{
parent::__construct($parent, $name);
}
public function injectAclFacade(\MTS\App\AclModule\Models\Facades\AclFacade $aclFacade)
{
if ($this->aclFacade !== NULL)
throw new \Nette\InvalidStateException('AclFacade has already been set.');
$this->aclFacade = $aclFacade;
}
public function create()
{
if ($this->aclFacade === NULL)
throw new \Nette\InvalidStateException('Dependencies have not been set.');
$this->addMultiSelect('roles', 'Role', NULL, 15)
->setRequired('Zvolte si role.');
$this->addMultiSelect('resources', 'Zdroje', NULL, 15)
->setRequired('Zvolte si zdroje.');
$this->addMultiSelect('privileges', 'Privilegia', NULL, 15)
->setRequired('Zvolte si privilegia.');
$this->addRadioList('permission', 'Přístup', array(1 => 'Povolit', 0 => 'Zakázat'))
->setRequired('Zvolte si typ pravidla.');
$this->addProtection('Bezpečnostní token vypršel. Ujistěte se, že máte povolené cookies, a odešlete formulář znovu.');
$this->addSubmit('send', 'Přidat');
}
public function storeAclRuleFormSubmitted(\Nette\Application\UI\Form $form)
{
if ($this->aclFacade === NULL)
throw new \Nette\InvalidStateException('Dependencies have not been set.');
$values = $form->getValues();
$this->getPresenter()->flashMessage('Pravidla byla přidána.', 'success');
$this->getPresenter()->redirect('default');
}
}
Předpokládám, že použití new v továrničce nesplňuje jakési striktní DI, takže bych tuto třídu měl předpokládám také předat jako závislost do presenteru?
Nelíbí se mi v té továrničce ani použití mých metod inject a pak následně create. Jde o to, že v create nebo obsluze odeslaného formuláře se závislosti někdy využívají. V ukázce zrovna ne, ale chtěl bych to obecně. V konstruktoru to využívat nechci, jelikož bych mohl narazit na problém s děděním a parametry konstruktoru předka.
Lze nějak do této komponenty vkládat závislosti podobně jako do presenteru?
Také bych se chtěl zeptat, jak správně vyřešit redirect a flashMessage bez getPresenter. Někde jsem četl, že to není optimální řešení.
Za odpověď děkuji.
Editoval darthcz (3. 2. 2013 6:23)
- Šaman
- Člen | 2665
Takže postupně:
- v Nette 2.0.x není možné používat injectXxx() metody mimo presentery.
(Resp. nefunguje na nich autowire. Je nutné je volat ručně jako obyčejný
setter, čímž tyto metody defacto jsou.) V
dev
verzi tato možnost je. - na povinné závislosti je vhodné používat konstruktor, na nepovinné
setter (jen u presenterů je tolerované se konstruktoru vyhnout, protože
presentery mají závislostí moc a zápis by byl nepřehledný). Tzn. že pokud
pro formulář
AclFacade
není životně důležitá služba a formulář se zvládne zpracovat bez ní, používáš správně setter. Pokud bez ní form nemůže existovat, nech si ji předat hned v konstruktoru. - vytváření instance v továrničce pomocí
new
je podle mě naprosto v pořádku. DI ti injectuje hotové třídy a služby, které si máš nechat PŘEDAT a nelovit je svépomocí někde v kontextu nebo čertví kde. Továrna slouží k VYTVOŘENÍ nové instance třídy. Teoreticky můžeš mít tuto továrničku jako službu v configu, pak presenteru předáváš pomocí DI službu, která ti ale opět vytvoří form pomocínew
. - flashMessage() je metoda presenteru a bez něho se zavolat nedá. Resp. dá se zavolat „malá“ flashMessage přímo nad komponentou (https://doc.nette.org/…n/components), pokud chceš ale volat flashMessage která by se vykreslila v pohledu presenteru, musíš ten presenter mít.
Editoval Šaman (3. 2. 2013 6:12)
- vvoody
- Člen | 910
Naimplementovať si inject autowire nad komponentami nieje až taký problém ak každú komponentu vytvárame továrnou triedou. Stačí opajcovať kúsok kódu z presenter factory :)
abstract class BaseFactory extends \Nette\Object
{
/** @var \Nette\DI\Container */
protected $container;
public function __construct(\Nette\DI\Container $c)
{
$this->container = $c;
}
protected function inject($object)
{
if(is_object($object)) {
foreach (array_reverse(get_class_methods($object)) as $method) {
if (substr($method, 0, 6) === 'inject') {
$this->container->callMethod(array($object, $method));
}
}
}
return $object;
}
}
class LoginFormFactory extends BaseFactory
{
public function create()
{
return $this->inject(new \App\Components\LoginForm);
}
}