Komponenty v DIC pomocí nových továrniček

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Filip Procházka
Moderator | 4668
+
0
-

Nedávno jsem tu zakládal flame, jak mít komponenty v DI Containeru. Doporučil bych pročíst, je tam spousta zajímavých postřehů.

Ovšem teprve s novým-novým DIC to teprve dává smysl! Všimli jste si také těch factories ? Možná vám taky, jako mně, nesedělo že bych si předával $context napříč modely a vytvářel si z něj entity. Divné by to bylo. Ale kde to dává smysl, tak to jsou komponenty.

Předvedu na příkladu:

factories:
	comp1:
		class: MyComponent

A v presenteru

protected function createComponentComp()
{
	return $this->getContext()->createComp1();
}

Naprostá bomba, že? Funguje v nich všechno, co by šlo nastavit v normální službě. Navíc máte k dispozici možnost nastavit vstupní parametry téhle továrničce a není sdílená. Vytvoří se pokaždé znovu. Což byla hlavní skulinka, kdyby byly komponenty v DIC jako služby.

To stejné jde dělat i s presentery! Stačí si podědit a upravit IPresenterFactory, jako inspiraci bych nabídl svoji implementaci.

Samozřejmě je potřeba překousnout fakt, že contextu se jen tak nezbavíme. Bylo by možné, komponenty do presenterů předávat automaticky už v DIC, třeba takto:

factories:
	myPresenter:
		class: MyPresenter(@container)
		setup:
			- [addComponent, [@gridComponent, 'grid']]
			- [addComponent, [@paginatorComponent, 'paginator']]

Jenomže tohle nás kompletně odstřihne od jakékoliv možnosti lazy inicializace.

Líbí se mi první varianta, kde si komponenty navazuji v továrničkách a vyrábím v contextu. Ten jeden context v těch presenterech klidně překousnu.


Zahodil jsem TemplateFactory, protože jsem nakonec pochopil, že je to hovadina a s matnou představou co by to mohl být ten templateConfigurator, který zmiňoval @**HonzaMarek**, jsem si udělal vlastní. Jde o třídu, která k vytvořené instanci šablony přidá globální makra a helpery.

Jako bonus bych nabídl pár řádků kódu, který vám pomůže komponenty pěkně sjednotit. Nebavilo mě totiž na každé komponentě volat ->setParameters() ikdyž žádné nemá a nebo ji vždy nastavovat jako ne-sdílenou, vypínat autowire a předávat templateConfigurator.

Když si tedy ke komponentě přidám tag component, tak bude nastavena tak, aby se chovala jako továrnička.

David Grudl
Nette Core | 8218
+
0
-

Továrnička pak jde úplně zobecnit na

protected function createComponent($name)
{
	return $this->getContext()->components->$name; // nebo nějak podobně
}

A v konfigu by to bylo jako

components:
	factories:
		signUpForm: ...
Filip Procházka
Moderator | 4668
+
0
-

To se mi už ale úplně nelíbí. Protože takhle bych vytvořil jakoukoliv komponentu v jakémkoliv presenteru. Určitě je to tak v pořádku? Jak budu ovládat přístup?

Třeba teď mám

	/**
	 * @return \Kdyby\Components\FrontCmsPanel\NodeDesignerControl
	 * @Allowed("cms:node", "create")
	 */
	protected function createComponentNodeDesigner()
	{
		return $this->getContainer()->createCms_nodeDesigner();
	}

A chrání mi to checkRequirements.

Takhle bych se o to připravil.

uestla
Backer | 799
+
0
-

David Grudl napsal(a):

Továrnička pak jde úplně zobecnit na

protected function createComponent($name)
{
	return $this->getContext()->components->$name; // nebo nějak podobně
}

Neházelo by to, že service neexistuje? Čili by se mělo volat $container->components->{'create' . $name}(); ?

David Grudl
Nette Core | 8218
+
0
-

Je potřeba dodat, že používání getContext() je v rozporu s Dependency Injection s výjimkou situace, kdy by samotný presenter byl v roli DI kontejneru. Což on skutečně z určitého pohledu je, problém je v tom, že zároveň plní i jiné úlohy.