Nette Documentation Preview

syntax
Wytworzone fabryki
******************

.[perex]
Nette DI może automatycznie generować kod fabryczny na podstawie interfejsu, oszczędzając na pisaniu kodu.

Fabryka to klasa, która tworzy i konfiguruje obiekty. W związku z tym przekazuje im również ich zależności. Proszę nie mylić z wzorcem projektowym *factory method*, który opisuje specyficzny sposób używania fabryk i nie jest związany z tym tematem.

Jak wygląda taka fabryka, pokazaliśmy w [rozdziale wprowadzającym |introduction#factory]:

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

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

Nette DI może automatycznie wygenerować kod fabryczny. Wystarczy, że stworzysz interfejs, a Nette DI wygeneruje jego implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie `create` i deklarować typ zwrotny:

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

Tak więc fabryka `ArticleFactory` ma metodę `create`, która tworzy obiekty `Article`. Klasa `Article` może wyglądać na przykład tak:

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

Dodaj fabrykę do pliku konfiguracyjnego:

```neon
services:
	- ArticleFactory
```

Nette DI wygeneruje odpowiednią implementację fabryczną.

W kodzie, który korzysta z fabryki, żądamy obiektu przez interfejs, a Nette DI używa wygenerowanej implementacji:

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

	public function foo()
	{
		// pozwól fabryce stworzyć obiekt
		$article = $this->articleFactory->create();
	}
}
```


Fabryka z parametrami .[#toc-parameterized-factory]
===================================================

Metoda fabryczna `create` może otrzymywać parametry, które następnie przekazuje do konstruktora. Na przykład uzupełnijmy klasę `Article` o ID autora artykułu:

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

Dodamy również parametr do fabryki:

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

Ponieważ parametr w konstruktorze i parametr w fabryce mają tę samą nazwę, Nette DI przekazuje je automatycznie.


Zaawansowana definicja .[#toc-advanced-definition]
==================================================

Definicja może być również zapisana w formie wieloliniowej za pomocą klawisza `implement`:

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

Pisząc w ten dłuższy sposób, można podać dodatkowe argumenty do konstruktora w kluczu `arguments` oraz dodatkową konfigurację za pomocą `setup`, tak jak w przypadku zwykłych serwisów.

Przykład: gdyby metoda `create()` nie akceptowała parametru `$authorId`, moglibyśmy w konfiguracji określić stałą wartość, która zostałaby przekazana do konstruktora `Article`:

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

Lub odwrotnie, gdyby `create()` zaakceptował parametr `$authorId`, ale nie był on częścią konstruktora i został przekazany przez metodę `Article::setAuthorId()`, odwołalibyśmy się do niego w sekcji `setup`:

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


Pomocnik .[#toc-accessor]
=========================

Nette oprócz fabryk może generować accessory. Są to obiekty z metodą `get()`, która zwraca określoną usługę z kontenera DI. Wielokrotne wywołanie `get()` wciąż zwraca tę samą instancję.

Accessory zapewniają leniwe ładowanie zależności. Rozważmy klasę, która zapisuje błędy do specjalnej bazy danych. Gdyby ta klasa miała połączenie z bazą danych przekazywane jako zależność przez konstruktor, połączenie zawsze musiałoby zostać nawiązane, choć w praktyce rzadko występowałby błąd, a więc przez większość czasu połączenie pozostawałoby nieużywane.
Więc zamiast tego klasa przekazuje accessor i tylko wtedy, gdy jego `get()` jest wywoływany, obiekt bazy danych zostaje utworzony:

Jak stworzyć accessora? Wystarczy napisać interfejs, a Nette DI wygeneruje jego implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie `get` i deklarować typ zwrotny:

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

Dodaj accessor do pliku konfiguracyjnego, który określa również usługę, którą zwróci:

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

Ponieważ accessor zwraca usługę typu `PDO` i w konfiguracji jest tylko jedna taka usługa, zwróci tę usługę. Jeśli istnieje więcej usług tego typu, określ usługę, która ma być zwrócona przez nazwę, np. `- PDOAccessor(@db1)`.


Wiele fabryk/akcesoriów .[#toc-multifactory-accessor]
=====================================================
Dotychczas nasze fabryki i accessory były w stanie wyprodukować lub zwrócić tylko jeden obiekt. Jednak bardzo łatwo jest stworzyć wiele fabryk połączonych z accessorami. Interfejs takiej klasy będzie zawierał dowolną liczbę metod o nazwach `create<name>()` a `get<name>()`, np:

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

Więc zamiast przekazywać kilka wygenerowanych fabryk i accessorów, przekazujemy jedną bardziej złożoną fabrykę, która może zrobić więcej.

Alternatywnie można użyć `get()` z parametrem zamiast wielu metod:

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

W tym przypadku `MultiFactory::getArticle()` robi to samo, co `MultiFactoryAlt::get('article')`. Alternatywna składnia ma jednak kilka wad. Nie jest jasne, które wartości `$name` są obsługiwane, a typ zwracany nie może być określony w interfejsie, gdy używanych jest wiele różnych wartości `$name`.


Definicja według listy .[#toc-definition-with-a-list]
-----------------------------------------------------
W ten sposób można zdefiniować wiele fabryk w konfiguracji: .{data-version:3.2.0}

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

Lub w definicji fabryki możemy odwołać się do istniejących usług za pomocą referencji:

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


Definicja według znaczników .[#toc-definition-with-tags]
--------------------------------------------------------

Druga opcja to użycie [tagów |services#Tags] do definicji:

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

Wytworzone fabryki

Nette DI może automatycznie generować kod fabryczny na podstawie interfejsu, oszczędzając na pisaniu kodu.

Fabryka to klasa, która tworzy i konfiguruje obiekty. W związku z tym przekazuje im również ich zależności. Proszę nie mylić z wzorcem projektowym factory method, który opisuje specyficzny sposób używania fabryk i nie jest związany z tym tematem.

Jak wygląda taka fabryka, pokazaliśmy w rozdziale wprowadzającym:

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

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

Nette DI może automatycznie wygenerować kod fabryczny. Wystarczy, że stworzysz interfejs, a Nette DI wygeneruje jego implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie create i deklarować typ zwrotny:

interface ArticleFactory
{
	function create(): Article;
}

Tak więc fabryka ArticleFactory ma metodę create, która tworzy obiekty Article. Klasa Article może wyglądać na przykład tak:

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

Dodaj fabrykę do pliku konfiguracyjnego:

services:
	- ArticleFactory

Nette DI wygeneruje odpowiednią implementację fabryczną.

W kodzie, który korzysta z fabryki, żądamy obiektu przez interfejs, a Nette DI używa wygenerowanej implementacji:

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

	public function foo()
	{
		// pozwól fabryce stworzyć obiekt
		$article = $this->articleFactory->create();
	}
}

Fabryka z parametrami

Metoda fabryczna create może otrzymywać parametry, które następnie przekazuje do konstruktora. Na przykład uzupełnijmy klasę Article o ID autora artykułu:

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

Dodamy również parametr do fabryki:

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

Ponieważ parametr w konstruktorze i parametr w fabryce mają tę samą nazwę, Nette DI przekazuje je automatycznie.

Zaawansowana definicja

Definicja może być również zapisana w formie wieloliniowej za pomocą klawisza implement:

services:
	articleFactory:
		implement: ArticleFactory

Pisząc w ten dłuższy sposób, można podać dodatkowe argumenty do konstruktora w kluczu arguments oraz dodatkową konfigurację za pomocą setup, tak jak w przypadku zwykłych serwisów.

Przykład: gdyby metoda create() nie akceptowała parametru $authorId, moglibyśmy w konfiguracji określić stałą wartość, która zostałaby przekazana do konstruktora Article:

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

Lub odwrotnie, gdyby create() zaakceptował parametr $authorId, ale nie był on częścią konstruktora i został przekazany przez metodę Article::setAuthorId(), odwołalibyśmy się do niego w sekcji setup:

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

Pomocnik

Nette oprócz fabryk może generować accessory. Są to obiekty z metodą get(), która zwraca określoną usługę z kontenera DI. Wielokrotne wywołanie get() wciąż zwraca tę samą instancję.

Accessory zapewniają leniwe ładowanie zależności. Rozważmy klasę, która zapisuje błędy do specjalnej bazy danych. Gdyby ta klasa miała połączenie z bazą danych przekazywane jako zależność przez konstruktor, połączenie zawsze musiałoby zostać nawiązane, choć w praktyce rzadko występowałby błąd, a więc przez większość czasu połączenie pozostawałoby nieużywane. Więc zamiast tego klasa przekazuje accessor i tylko wtedy, gdy jego get() jest wywoływany, obiekt bazy danych zostaje utworzony:

Jak stworzyć accessora? Wystarczy napisać interfejs, a Nette DI wygeneruje jego implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie get i deklarować typ zwrotny:

interface PDOAccessor
{
	function get(): PDO;
}

Dodaj accessor do pliku konfiguracyjnego, który określa również usługę, którą zwróci:

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

Ponieważ accessor zwraca usługę typu PDO i w konfiguracji jest tylko jedna taka usługa, zwróci tę usługę. Jeśli istnieje więcej usług tego typu, określ usługę, która ma być zwrócona przez nazwę, np. - PDOAccessor(@db1).

Wiele fabryk/akcesoriów

Dotychczas nasze fabryki i accessory były w stanie wyprodukować lub zwrócić tylko jeden obiekt. Jednak bardzo łatwo jest stworzyć wiele fabryk połączonych z accessorami. Interfejs takiej klasy będzie zawierał dowolną liczbę metod o nazwach create<name>() a get<name>(), np:

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

Więc zamiast przekazywać kilka wygenerowanych fabryk i accessorów, przekazujemy jedną bardziej złożoną fabrykę, która może zrobić więcej.

Alternatywnie można użyć get() z parametrem zamiast wielu metod:

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

W tym przypadku MultiFactory::getArticle() robi to samo, co MultiFactoryAlt::get('article'). Alternatywna składnia ma jednak kilka wad. Nie jest jasne, które wartości $name są obsługiwane, a typ zwracany nie może być określony w interfejsie, gdy używanych jest wiele różnych wartości $name.

Definicja według listy

W ten sposób można zdefiniować wiele fabryk w konfiguracji:

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

Lub w definicji fabryki możemy odwołać się do istniejących usług za pomocą referencji:

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

Definicja według znaczników

Druga opcja to użycie tagów do definicji:

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