Nette Documentation Preview

syntax
Generované továrny
******************

.[perex]
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. Nezaměňujte prosím s návrhovým vzorem *factory method*, který popisuje specifický způsob využití továren a s tímto tématem nesouvisí.

Jak taková továrna vypadá jsme si ukázali v [úvodní kapitole|introduction#továrna]:

```php
class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}
```

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:

```php
interface ArticleFactory
{
	function create(): Article;
}
```

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

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}
```

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

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

```php
class UserController
{
	public function __construct(
		private 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:

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}
```

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

```php
interface ArticleFactory
{
	function create(int $authorId): Article;
}
```

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

```neon
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`:

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

Nebo naopak pokud by `create()` parametr `$authorId` přijimala, ale nebyl by součástí konstruktoru a předával se metodou `Article::setAuthorId()`, odkázali bychom se na něj v sekci `setup`:

```neon
services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)
```


Accessor
========

Nette umí krom továren generovat i tzv. accessory. Jde o objekty s metodou `get()`, která vrací určitou službu z DI kontejneru. Opakované volání `get()` vrací stále tutéž instanci.

Accessor poskytují závislostem lazy-loading. Mějme třídu, která zapisuje chyby do speciální databáze. Když by si tato třída nechávala připojení k databázi předávat jako závislost konstruktorem, muselo by se připojení vždycky vytvořit, ačkoliv v praxi se chyba objeví jen výjimečně a tedy povětšinou by zůstalo spojení nevyužité.
Místo toho si tak třída předá accessor a teprve když se zavolá jeho `get()`, dojde k vytvoření objektu databáze:

Jak accessor vytvořit? Stačí napsat rozhraní a Nette DI vygeneruje implementaci. Rozhraní musí mít přesně jednu metodu s názvem `get` a deklarovat návratový typ:

```php
interface PDOAccessor
{
	function get(): PDO;
}
```

Accessor přidáme do konfiguračního souboru, kde je také definice služby, kterou bude vracet:

```neon
services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)
```

Protože accessor vrací službu typu `PDO` a v konfiguraci je jediná taková služba, bude vracet právě ji. Pokud by služeb daného typu bylo víc, určíme vracenou službu pomocí názvu, např. `- PDOAccessor(@db1)`.


Vícenásobná továrna/accessor
============================
Naše továrny a accessory uměly zatím vždy vyrábět nebo vracet jen jeden objekt. Lze ale velmi snadno vytvořit i vícenásobné továrny kombinované s accessory. Rozhraní takové třídy bude obsahovat libovolný počet metod s názvy `create<name>()` a `get<name>()`, např.:

```php
interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}
```

Takže místo toho, abych si předávali několik generovaných továren a accessorů, předáme jednu komplexnější továrnu, která toho umí víc.

Alternativně lze místo několika metod použít `get()` s parameterem:

```php
interface MultiFactoryAlt
{
	function get($name): PDO;
}
```

Pak platí, že `MultiFactory::getArticle()` dělá totéž jako `MultiFactoryAlt::get('article')`. Nicméně alternativní zápis má tu nevýhodu, že není zřejmé, jaké hodnoty `$name` jsou podporované a logicky také nelze v rozhraní odlišit různé návratové hodnoty pro různé `$name`.


Definice seznamem
-----------------
Tímto způsobem lze definovat vícenásobnou továrnu v konfiguraci: .{data-version:3.2.0}

```neon
services:
	- MultiFactory(
		article: Article                      # definuje createArticle()
		db: PDO(%dsn%, %user%, %password%)    # definuje getDb()
	)
```

Nebo se můžeme v definici továrny odkázat na existující služby pomocí reference:

```neon
services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # definuje createArticle()
		db: @\PDO            # definuje getDb()
	)
```


Definice pomocí tagů
--------------------

Druhou možností je využít k definici [tagy|services#Tagy]:

```neon
services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
```

Generované 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. Nezaměňujte prosím s návrhovým vzorem factory method, který popisuje specifický způsob využití továren a s tímto tématem nesouvisí.

Jak taková továrna vypadá jsme si ukázali v úvodní kapitole:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

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
{
	function create(): Article;
}

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

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

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

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
{
	public function __construct(
		private 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
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

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

interface ArticleFactory
{
	function create(int $authorId): Article;
}

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

Nebo naopak pokud by create() parametr $authorId přijimala, ale nebyl by součástí konstruktoru a předával se metodou Article::setAuthorId(), odkázali bychom se na něj v sekci setup:

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

Accessor

Nette umí krom továren generovat i tzv. accessory. Jde o objekty s metodou get(), která vrací určitou službu z DI kontejneru. Opakované volání get() vrací stále tutéž instanci.

Accessor poskytují závislostem lazy-loading. Mějme třídu, která zapisuje chyby do speciální databáze. Když by si tato třída nechávala připojení k databázi předávat jako závislost konstruktorem, muselo by se připojení vždycky vytvořit, ačkoliv v praxi se chyba objeví jen výjimečně a tedy povětšinou by zůstalo spojení nevyužité. Místo toho si tak třída předá accessor a teprve když se zavolá jeho get(), dojde k vytvoření objektu databáze:

Jak accessor vytvořit? Stačí napsat rozhraní a Nette DI vygeneruje implementaci. Rozhraní musí mít přesně jednu metodu s názvem get a deklarovat návratový typ:

interface PDOAccessor
{
	function get(): PDO;
}

Accessor přidáme do konfiguračního souboru, kde je také definice služby, kterou bude vracet:

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

Protože accessor vrací službu typu PDO a v konfiguraci je jediná taková služba, bude vracet právě ji. Pokud by služeb daného typu bylo víc, určíme vracenou službu pomocí názvu, např. - PDOAccessor(@db1).

Vícenásobná továrna/accessor

Naše továrny a accessory uměly zatím vždy vyrábět nebo vracet jen jeden objekt. Lze ale velmi snadno vytvořit i vícenásobné továrny kombinované s accessory. Rozhraní takové třídy bude obsahovat libovolný počet metod s názvy create<name>() a get<name>(), např.:

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

Takže místo toho, abych si předávali několik generovaných továren a accessorů, předáme jednu komplexnější továrnu, která toho umí víc.

Alternativně lze místo několika metod použít get() s parameterem:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

Pak platí, že MultiFactory::getArticle() dělá totéž jako MultiFactoryAlt::get('article'). Nicméně alternativní zápis má tu nevýhodu, že není zřejmé, jaké hodnoty $name jsou podporované a logicky také nelze v rozhraní odlišit různé návratové hodnoty pro různé $name.

Definice seznamem

Tímto způsobem lze definovat vícenásobnou továrnu v konfiguraci:

services:
	- MultiFactory(
		article: Article                      # definuje createArticle()
		db: PDO(%dsn%, %user%, %password%)    # definuje getDb()
	)

Nebo se můžeme v definici továrny odkázat na existující služby pomocí reference:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # definuje createArticle()
		db: @\PDO            # definuje getDb()
	)

Definice pomocí tagů

Druhou možností je využít k definici tagy:

services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)