Nette Documentation Preview

syntax
Generált factory-k
******************

.[perex]
A Nette DI képes automatikusan generálni factory kódot interfészek alapján, ami megkíméli Önt a kódírástól.

A factory egy olyan osztály, amely objektumokat gyárt és konfigurál. Tehát átadja nekik a függőségeiket is. Kérjük, ne keverje össze a *factory method* tervezési mintával, amely a factory-k specifikus felhasználási módját írja le, és nem kapcsolódik ehhez a témához.

Hogy néz ki egy ilyen factory, azt a [bevezető fejezetben |introduction#Factory] mutattuk be:

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

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

A Nette DI képes automatikusan generálni a factory kódot. Mindössze annyit kell tennie, hogy létrehoz egy interfészt, és a Nette DI legenerálja az implementációt. Az interfésznek pontosan egy `create` nevű metódussal kell rendelkeznie, és deklarálnia kell a visszatérési típust:

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

Tehát az `ArticleFactory` factorynak van egy `create` metódusa, amely `Article` objektumokat hoz létre. Az `Article` osztály például így nézhet ki:

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

A factoryt hozzáadjuk a konfigurációs fájlhoz:

```neon
services:
	- ArticleFactory
```

A Nette DI legenerálja a factory megfelelő implementációját.

A kódban, amely a factoryt használja, így kérünk egy objektumot az interfész alapján, és a Nette DI a generált implementációt használja:

```php
class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// hagyjuk, hogy a factory létrehozza az objektumot
		$article = $this->articleFactory->create();
	}
}
```


Paraméterezett factory
======================

A `create` factory metódus elfogadhat paramétereket, amelyeket aztán átad a konstruktornak. Egészítsük ki például az `Article` osztályt a cikk szerzőjének ID-jával:

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

A paramétert hozzáadjuk a factoryhoz is:

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

Annak köszönhetően, hogy a konstruktorban és a factoryban lévő paraméter neve ugyanaz, a Nette DI teljesen automatikusan átadja őket.


Haladó definíció
================

A definíciót többsoros formában is le lehet írni az `implement` kulcs használatával:

```neon
services:
	articleFactory:
		implement: ArticleFactory
```

Ezzel a hosszabb írásmóddal további argumentumokat lehet megadni a konstruktorhoz az `arguments` kulcsban, és kiegészítő konfigurációt a `setup` segítségével, ugyanúgy, mint a [normál szolgáltatásoknál|services].

Példa: ha a `create()` metódus nem fogadná el a `$authorId` paramétert, megadhatnánk egy fix értéket a konfigurációban, amelyet átadnánk az `Article` konstruktorának:

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

Vagy fordítva, ha a `create()` elfogadná a `$authorId` paramétert, de az nem lenne része a konstruktornak, és a `Article::setAuthorId()` metódussal adnánk át, akkor a `setup` szekcióban hivatkoznánk rá:

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


Accessor
========

A Nette a factory-k mellett ún. accessorokat is tud generálni. Ezek olyan objektumok, amelyeknek van egy `get()` metódusa, amely egy bizonyos szolgáltatást ad vissza a DI konténerből. A `get()` ismételt hívása mindig ugyanazt a példányt adja vissza.

Az accessorok lazy-loadingot biztosítanak a függőségeknek. Tegyük fel, hogy van egy osztályunk, amely hibákat ír egy speciális adatbázisba. Ha ez az osztály konstruktorfüggőségként kapná meg az adatbázis-kapcsolatot, a kapcsolatot mindig létre kellene hozni, bár a gyakorlatban hiba csak kivételesen fordul elő, és így a kapcsolat legtöbbször kihasználatlan maradna. Ehelyett az osztály átad egy accessort, és csak akkor jön létre az adatbázis objektum, amikor annak `get()` metódusát meghívják:

Hogyan hozzunk létre accessort? Csak írjunk egy interfészt, és a Nette DI legenerálja az implementációt. Az interfésznek pontosan egy `get` nevű metódussal kell rendelkeznie, és deklarálnia kell a visszatérési típust:

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

Az accessort hozzáadjuk a konfigurációs fájlhoz, ahol a szolgáltatás definíciója is található, amelyet vissza fog adni:

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

Mivel az accessor `PDO` típusú szolgáltatást ad vissza, és a konfigurációban csak egy ilyen szolgáltatás van, pontosan azt fogja visszaadni. Ha több ilyen típusú szolgáltatás lenne, a visszaadott szolgáltatást név szerint határoznánk meg, pl. `- PDOAccessor(@db1)`.


Többszörös factory/accessor
===========================
Eddig a factory-ink és accessoraink mindig csak egy objektumot tudtak gyártani vagy visszaadni. De nagyon könnyen létrehozhatunk többszörös factory-kat is accessorokkal kombinálva. Egy ilyen osztály interfésze tetszőleges számú `create<name>()` és `get<name>()` nevű metódust tartalmazhat, pl.:

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

Tehát ahelyett, hogy több generált factoryt és accessort adnánk át, egy komplexebb factoryt adunk át, amely többet tud.

Alternatívaként több metódus helyett használhatjuk a `get()`-et paraméterrel:

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

Ekkor igaz, hogy a `MultiFactory::getArticle()` ugyanazt csinálja, mint a `MultiFactoryAlt::get('article')`. Az alternatív írásmódnak azonban az a hátránya, hogy nem egyértelmű, milyen `$name` értékek támogatottak, és logikailag nem lehet megkülönböztetni a különböző visszatérési értékeket a különböző `$name`-ekhez az interfészben.


Definíció listával
------------------
Ezzel a módszerrel definiálhatunk többszörös factoryt a konfigurációban: .{data-version:3.2.0}

```neon
services:
	- MultiFactory(
		article: Article                      # definiálja a createArticle()-t
		db: PDO(%dsn%, %user%, %password%)    # definiálja a getDb()-t
	)
```

Vagy a factory definíciójában hivatkozhatunk létező szolgáltatásokra referenciával:

```neon
services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # definiálja a createArticle()-t
		db: @\PDO            # definiálja a getDb()-t
	)
```


Definíció tagekkel
------------------

A második lehetőség a [tageket |services#Tagek] használni a definícióhoz:

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

Generált factory-k

A Nette DI képes automatikusan generálni factory kódot interfészek alapján, ami megkíméli Önt a kódírástól.

A factory egy olyan osztály, amely objektumokat gyárt és konfigurál. Tehát átadja nekik a függőségeiket is. Kérjük, ne keverje össze a factory method tervezési mintával, amely a factory-k specifikus felhasználási módját írja le, és nem kapcsolódik ehhez a témához.

Hogy néz ki egy ilyen factory, azt a bevezető fejezetben mutattuk be:

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

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

A Nette DI képes automatikusan generálni a factory kódot. Mindössze annyit kell tennie, hogy létrehoz egy interfészt, és a Nette DI legenerálja az implementációt. Az interfésznek pontosan egy create nevű metódussal kell rendelkeznie, és deklarálnia kell a visszatérési típust:

interface ArticleFactory
{
	function create(): Article;
}

Tehát az ArticleFactory factorynak van egy create metódusa, amely Article objektumokat hoz létre. Az Article osztály például így nézhet ki:

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

A factoryt hozzáadjuk a konfigurációs fájlhoz:

services:
	- ArticleFactory

A Nette DI legenerálja a factory megfelelő implementációját.

A kódban, amely a factoryt használja, így kérünk egy objektumot az interfész alapján, és a Nette DI a generált implementációt használja:

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// hagyjuk, hogy a factory létrehozza az objektumot
		$article = $this->articleFactory->create();
	}
}

Paraméterezett factory

A create factory metódus elfogadhat paramétereket, amelyeket aztán átad a konstruktornak. Egészítsük ki például az Article osztályt a cikk szerzőjének ID-jával:

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

A paramétert hozzáadjuk a factoryhoz is:

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

Annak köszönhetően, hogy a konstruktorban és a factoryban lévő paraméter neve ugyanaz, a Nette DI teljesen automatikusan átadja őket.

Haladó definíció

A definíciót többsoros formában is le lehet írni az implement kulcs használatával:

services:
	articleFactory:
		implement: ArticleFactory

Ezzel a hosszabb írásmóddal további argumentumokat lehet megadni a konstruktorhoz az arguments kulcsban, és kiegészítő konfigurációt a setup segítségével, ugyanúgy, mint a normál szolgáltatásoknál.

Példa: ha a create() metódus nem fogadná el a $authorId paramétert, megadhatnánk egy fix értéket a konfigurációban, amelyet átadnánk az Article konstruktorának:

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

Vagy fordítva, ha a create() elfogadná a $authorId paramétert, de az nem lenne része a konstruktornak, és a Article::setAuthorId() metódussal adnánk át, akkor a setup szekcióban hivatkoznánk rá:

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

Accessor

A Nette a factory-k mellett ún. accessorokat is tud generálni. Ezek olyan objektumok, amelyeknek van egy get() metódusa, amely egy bizonyos szolgáltatást ad vissza a DI konténerből. A get() ismételt hívása mindig ugyanazt a példányt adja vissza.

Az accessorok lazy-loadingot biztosítanak a függőségeknek. Tegyük fel, hogy van egy osztályunk, amely hibákat ír egy speciális adatbázisba. Ha ez az osztály konstruktorfüggőségként kapná meg az adatbázis-kapcsolatot, a kapcsolatot mindig létre kellene hozni, bár a gyakorlatban hiba csak kivételesen fordul elő, és így a kapcsolat legtöbbször kihasználatlan maradna. Ehelyett az osztály átad egy accessort, és csak akkor jön létre az adatbázis objektum, amikor annak get() metódusát meghívják:

Hogyan hozzunk létre accessort? Csak írjunk egy interfészt, és a Nette DI legenerálja az implementációt. Az interfésznek pontosan egy get nevű metódussal kell rendelkeznie, és deklarálnia kell a visszatérési típust:

interface PDOAccessor
{
	function get(): PDO;
}

Az accessort hozzáadjuk a konfigurációs fájlhoz, ahol a szolgáltatás definíciója is található, amelyet vissza fog adni:

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

Mivel az accessor PDO típusú szolgáltatást ad vissza, és a konfigurációban csak egy ilyen szolgáltatás van, pontosan azt fogja visszaadni. Ha több ilyen típusú szolgáltatás lenne, a visszaadott szolgáltatást név szerint határoznánk meg, pl. - PDOAccessor(@db1).

Többszörös factory/accessor

Eddig a factory-ink és accessoraink mindig csak egy objektumot tudtak gyártani vagy visszaadni. De nagyon könnyen létrehozhatunk többszörös factory-kat is accessorokkal kombinálva. Egy ilyen osztály interfésze tetszőleges számú create<name>() és get<name>() nevű metódust tartalmazhat, pl.:

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

Tehát ahelyett, hogy több generált factoryt és accessort adnánk át, egy komplexebb factoryt adunk át, amely többet tud.

Alternatívaként több metódus helyett használhatjuk a get()-et paraméterrel:

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

Ekkor igaz, hogy a MultiFactory::getArticle() ugyanazt csinálja, mint a MultiFactoryAlt::get('article'). Az alternatív írásmódnak azonban az a hátránya, hogy nem egyértelmű, milyen $name értékek támogatottak, és logikailag nem lehet megkülönböztetni a különböző visszatérési értékeket a különböző $name-ekhez az interfészben.

Definíció listával

Ezzel a módszerrel definiálhatunk többszörös factoryt a konfigurációban:

services:
	- MultiFactory(
		article: Article                      # definiálja a createArticle()-t
		db: PDO(%dsn%, %user%, %password%)    # definiálja a getDb()-t
	)

Vagy a factory definíciójában hivatkozhatunk létező szolgáltatásokra referenciával:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # definiálja a createArticle()-t
		db: @\PDO            # definiálja a getDb()-t
	)

Definíció tagekkel

A második lehetőség a tageket használni a definícióhoz:

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