Generált gyárak

A Nette DI képes automatikusan gyárkódot generálni a felület alapján, ami megkíméli Önt a kód írásától.

A gyár egy olyan osztály, amely objektumokat hoz létre és konfigurál. Ezért a függőségeiket is átadja nekik. Kérjük, ne keverjük össze a factory method tervezési mintával, amely a gyárak használatának egy speciális módját írja le, és nem kapcsolódik ehhez a témához.

bevezető fejezetben megmutattuk, hogyan néz ki egy ilyen gyár:

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 gyárkódot generálni. Csak egy interfészt kell létrehozni, és a Nette DI generál egy implementációt. Az interfésznek pontosan egy create nevű metódussal kell rendelkeznie, és deklarálnia kell egy visszatérési típust:

interface ArticleFactory
{
	function create(): Article;
}

Tehát a ArticleFactory gyárnak van egy create metódusa, amely létrehozza a Article objektumokat. A Article osztály például a következőképpen nézhet ki:

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

Adja hozzá a gyárat a konfigurációs fájlhoz:

services:
	- ArticleFactory

A Nette DI létrehozza a megfelelő gyár implementációját.

Így a gyárat használó kódban az objektumot interfész szerint kérjük, é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 gyár létrehozzon egy objektumot
		$article = $this->articleFactory->create();
	}
}

Paraméteres gyár

A create gyári metódus paramétereket fogadhat el, amelyeket aztán átad a konstruktornak. Adjunk például egy cikk szerzői azonosítót a Article osztályhoz:

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

A paramétert is hozzáadjuk a gyárhoz:

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

Mivel a konstruktorban lévő paraméter és a gyárban lévő paraméter neve megegyezik, a Nette DI automatikusan átadja őket.

Speciális definíció

A definíciót többsoros formában is meg lehet írni a implement billentyűvel:

services:
	articleFactory:
		implement: ArticleFactory

Ha ilyen hosszabb formában írjuk, a arguments kulcsban további argumentumokat adhatunk meg a konstruktor számára, és a setup segítségével további konfigurációkat adhatunk meg, ugyanúgy, mint a normál szolgáltatások esetében.

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

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

Vagy fordítva, ha a create() elfogadja a $authorId paramétert, de az nem része a konstruktornak, hanem a Article::setAuthorId() módszer adja át, akkor a setup szakaszban hivatkoznánk rá:

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

Accessor

A gyárak mellett a Nette képes úgynevezett accessorokat is generálni. Az accessor egy olyan objektum, amely a get() metódussal rendelkezik, és egy adott szolgáltatást ad vissza a DI konténerből. Több get() hívás mindig ugyanazt a példányt adja vissza.

Az accessorok a lazy-loadingot hozzák a függőségekbe. Legyen egy osztályunk, amely a hibákat egy speciális adatbázisba naplózza. Ha az adatbázis-kapcsolatot függőségként adnánk át a konstruktorában, a kapcsolatot mindig létre kellene hozni, bár csak ritkán, hiba esetén használnánk, így a kapcsolat többnyire kihasználatlan maradna. Ehelyett az osztály átadhat egy accessort, és amikor annak get() metódusa meghívásra kerül, csak ekkor jön létre az adatbázis-objektum:

Hogyan hozzunk létre egy accessort? Írjunk csak 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;
}

Adja hozzá az elérőt a konfigurációs fájlhoz annak a szolgáltatásnak a definíciójával együtt, amelyet az elérő visszaad:

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

Az accessor egy PDO típusú szolgáltatást ad vissza, és mivel csak egy ilyen szolgáltatás van a konfigurációban, az accessor azt fogja visszaadni. Több ilyen típusú konfigurált szolgáltatás esetén a névvel megadhatja, hogy melyik legyen a visszaadandó szolgáltatás, például - PDOAccessor(@db1).

Multifactory/Accessor

Eddig a factories és accessorok csak egy objektumot tudtak létrehozni vagy visszaadni. Létrehozható egy multifactory egy accessorral kombinálva is. Az ilyen multifactory osztály interfésze több metódusból állhat, amelyek neve create<name>() és get<name>()például:

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

Ahelyett, hogy több generált gyárat és accessort adna át, csak egy összetett multifactory-t adhat át.

Alternatívaként használhatja a get() címet is egy paraméterrel, több metódus helyett:

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

Ebben az esetben a MultiFactory::getArticle() ugyanazt teszi, mint a MultiFactoryAlt::get('article'). Az alternatív szintaxisnak azonban van néhány hátránya. Nem egyértelmű, hogy mely $name értékek támogatottak, és a visszatérési típus nem adható meg az interfészben, ha több különböző $name értéket használunk.

Definíció listával

Így több gyárat lehet definiálni a konfigurációban:

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

Vagy a gyár definíciójában hivatkozással hivatkozhatunk meglévő szolgáltatásokra:

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

Meghatározás címkékkel

Egy másik lehetőség a multifactory definiálására a címkék használata:

services:
	- App\Router\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
verzió: 3.x