DI: Továrny

Nette DI umí automaticky generovat kód továren na základě rozhraní, což vám ušetří psaní kódu.

Továrna je třída, která vyrábí a konfiguruje objekty. Předává jim tedy i jejich závislosti. Jak taková továrna vypadá jsme si ukázali v úvodní kapitole.

Nette DI umí kód továren automaticky generovat. Vše, co musíte udělat, je vytvořit rozhraní a Nette DI vygeneruje implementaci. Rozhraní musí mít přesně jednu metodu s názvem create a deklarovat návratový typ:

interface ArticleFactory
{
	/** @return Article */
	function create();
}

Tedy továrna ArticleFactory má metodu create, která vytváří objekty Article. Třída Article může vypadat třeba následovně:

class Article
{
	private $db;

	public function __construct(Nette\Database\Connection $db)
	{
		$this->db = $db;
	}
}

Továrnu přidáme do konfiguračního souboru config.neon:

services:
	- ArticleFactory

Nette DI vygeneruje odpovídající implementaci továrny.

V kódu, který továrnu používá, si tak vyžádáme objekt podle rozhraní a Nette DI použije vygenerovanou implementaci:

class UserController
{
	private $articleFactory;

	public function __construct(ArticleFactory $articleFactory)
	{
		$this->articleFactory = $articleFactory;
	}

	public function foo()
	{
		// necháme továrnu vytvořit objekt
		$article = $this->articleFactory->create();
	}
}

Parametrizovaná továrna

Tovární metoda create může přijímat parametry, které poté předá do konstrukturu. Doplňme například třídu Article o ID autora článku:

class Article
{
	private $db;
	private $authorId;

	public function __construct(Nette\Database\Connection $db, $authorId)
	{
		$this->db = $db;
		$this->authorId = $authorId;
	}
}

Parametr přidáme také do továrny:

interface ArticleFactory
{
	/** @return Article */
	function create(int $authorId);
}

Díky tomu, že se parametr v konstruktoru i parametr v továrně jmenují stejně, Nette DI je zcela automaticky předá.

Pokročilá definice

Definici lze zapsat i ve víceřádkové podobě za použití klíče implement:

services:
	articleFactory:
		implement: ArticleFactory

Při zápisu tímto delším způsobem je možné uvést další argumenty pro konstruktor v klíči arguments a doplňující konfiguraci pomocí setup, stejně, jako u běžných služeb.

Příklad: pokud by metoda create() nepřijímala parametr $authorId, mohli bychom uvést pevnou hodnotu v konfiguraci, která by se předávala do konstruktoru Article:

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

Továrna na komponenty

Uvažujme následující komponentu, která má závislost na Service:

class MyControl extends Nette\Application\UI\Control
{
	private $service;

	public function __construct(Service $service)
	{
		$this->service = $service;
	}
}

Komponenty jsou typicky vytvářeny přímo v kódu presenteru či nadřazené komponenty. V takovém případě Nette DI nemůže závislosti automaticky předat, získáme je tedy ve třídě, která danou komponentu vytváří a předáme komponentě:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $service;

	public function __construct(Service $service)
	{
		$this->service = $service;
	}

	protected function createComponentMyControl()
	{
		$control = new MyControl($this->service);
		return $control;
	}
}

Potože komponenta může mít závislostí více, má i v těchto případech smysl si vytvořit pro komponentu továrnu. Můžeme si ji opět nechat vygenerovat na základě rozhraní. Naše rozhraní pro komponentu může vypadat například takto:

interface MyControlFactory
{
	/** @return MyControl */
	public function create();
}

Továrnu zaregistrujeme v konfiguračním souboru:

services:
	- MyControlFactory

V presenteru pak bude stačit získat továrnu a zavolat metodu create:

class MyPresenter extends Nette\Application\UI\Presenter
{
	private $myControlFactory;

	public function __construct(MyControlFactory $myControlFactory)
	{
		$this->myControlFactory = $myControlFactory;
	}

	protected function createComponentMyControl()
	{
		$control = $this->myControlFactory->create();
		return $control;
	}
}

Vytvořené komponentě budou automaticky konstruktorem předány její závislosti.