Jaký je Best practice DI modelu
- Richard Faila
- Člen | 40
Zdravím
Dejme tomu, že mám třídu Articles
, které má metody jako
getArticeById
nebo getAllArticles
a je registrovaná
jako service v config.neon
Mám teď trochu nepořádek v tom, jak dostat takový Model do Presenteru. Co jsem tak četl, tak obecně existují čtyři možnosti.
Přes $this->getService
<?php
class HomepagePresenter extends BasePresenter{
public function renderDefault(){
$this->template->articles = $this->getService('articles')->getAllArticles();
}
}
?>
Takto to zatím dělám já a nemám s tím problém.
Přes $this->context
<?php
class HomepagePresenter extends BasePresenter{
public function renderDefault(){
$this->template->articles = $this->context->articles->getAllArticles();
}
}
?>
Tohle se mi moc nelíbí, protože jsem někde četl, že se $this->context se nemá používat.
Předat Articles přes konstruktor
<?php
class HomepagePresenter extends BasePresenter{
private $articles;
public function __construct(Articles $articles){
parent::__construct()
$this->articles = $articles;
}
public function renderDefault(){
$this->template->articles = $this->articles->getAllArticles();
}
}
?>
Přes inject
<?php
class HomepagePresenter extends BasePresenter{
private $articles;
public function injectArticles(Articles $articles){
$this->articles = $articles;
}
public function renderDefault(){
$this->template->articles = $this->articles->getAllArticles();
}
}
?>
Tohle je nové a moc jsem na to zatím nenarazil. Tak nějak intuitivně
vím, že by to asi mělo být nejlepší řešení, ale zase nevím o kolik je
to lepší, než prosté $this->getService
. Často se mi
stává, že pro data sahám do více (=tří) různých modelů a tady bych si
musel vždy deklarovat tři proměnné a přidávat tři inject
metody.
Je tedy přístup přes $this->getService opravdu špatný nebo záleží na použití?
Vím, že se o této problematice poslední dobou hodně mluví a opravdu nechci vyvolávat žádný flame a pokud již to bylo někde vyřešeno, nekamenujte mě prosím, protože se mi nepodařilo najít žádné oficiální potvrzení.
Tak jak to tedy vlastně správně?
Děkuji
- Vojtěch Dobeš
- Gold Partner | 1316
Když si odmyslíš, že presenter je presenter, a budeš ho brát jako
jakoukoliv jinou třídu, tak obecně platí, že je správné si transparentně
předávat závislosti (tj. když jedna třída potřebuje instanci jiné
třídy atd.). V tvém výčtu chybí nejhorší pradávná varianta
Environment::getService(...
, to taky existovalo :).
$this->context
je o maličko lepší, ale furt to odporuje
transparentnímu předávání závislostí (které se dělá buď přes
konstruktor nebo skrze nějakou metodu, obvykle setSomething
–
setter). $this->getService()
je pouze obálka nad
$this->context
.
A teď proč jakési inject
. Je to proto, že presentery (jak
sám píšeš) mívají hodně závislostí, a zároveň od sebe dost často
dědí. Samotný Nette\Application\UI\Presenter
má dost
závislostí. Kdyby se tyto závislosti předávaly tím nejlepším možným
způsobem, tedy konstruktorem, byl by s tím oser při dědění (které je jak
říkám zatím velmi častou praktikou).
Proto byly zavedeny inject
metody, což jsou de facto obyčejné
settery, ale Nette ví, že je má při vytváření presenterů zavolat a
předá jim správné argumenty. A tak se nepoužívá
$this->context
. Což znamená že třeba v testech lze takový
presenter jednoduše sestavit, že je přehledné, co třída vyžaduje (stačí
když se podívám, jaké má inject metody… nemusím v kódu hledat
getService
)…
- Filip Procházka
- Moderator | 4668
Nejčistější je předávat závislosti přes konstruktor,
doporučované je předávat závislosti přes
inject*()
.
To že budeš mít více inject*()
nemá na nic vliv. Bude to
fungovat i když tam budeš mít jednu nebo 1000.
Můžeš zkusit tohle. Sice to využívá DIC jako service locator, ale je to velice pohodlné a neupíšeš se :)