Jaký je Best practice DI modelu

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

Zdravím

Dejme tomu, že mám třídu Articles, které má metody jako getArticeById nebo getAllArticles a je registrovaná jako serviceconfig.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
+
0
-

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)…

Tabetha
Člen | 140
+
0
-

a moja otázka … ked som skúšal injecty tak to na jednoduchých modeloch išlo … akonáhle som do modelu dával nejaké dalšie služby tak vtedy sa mi to nenačítalo … takže ak mám tie služby a pomerne prepletené tak aký spôsob by sa mal použiť? … teraz mám $this->context->*

Filip Procházka
Moderator | 4668
+
0
-

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 :)