Nette Documentation Preview

syntax
Generowane fabryki
******************

.[perex]
Nette DI potrafi automatycznie generować kod fabryk na podstawie interfejsów, co oszczędza Ci pisania kodu.

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

Jak wygląda taka fabryka, pokazaliśmy w [rozdziale wstępnym |introduction#Fabryka]:

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

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

Nette DI potrafi automatycznie generować kod fabryk. Wszystko, co musisz zrobić, to utworzyć interfejs, a Nette DI wygeneruje implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie `create` i deklarować typ zwracany:

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

Czyli fabryka `ArticleFactory` ma metodę `create`, która tworzy obiekty `Article`. Klasa `Article` może wyglądać na przykład następująco:

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

Fabrykę dodajemy do pliku konfiguracyjnego:

```neon
services:
	- ArticleFactory
```

Nette DI wygeneruje odpowiednią implementację fabryki.

W kodzie, który używa fabryki, żądamy obiektu według interfejsu, a Nette DI użyje wygenerowanej implementacji:

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

	public function foo()
	{
		// zlecamy fabryce utworzenie obiektu
		$article = $this->articleFactory->create();
	}
}
```


Fabryka sparametryzowana
========================

Metoda fabryczna `create` może przyjmować parametry, które następnie przekaże do konstruktora. Uzupełnijmy na przykład klasę `Article` o ID autora artykułu:

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

Parametr dodamy również do fabryki:

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

Dzięki temu, że parametr w konstruktorze i parametr w fabryce nazywają się tak samo, Nette DI przekaże je całkowicie automatycznie.


Definicja zaawansowana
======================

Definicję można zapisać również w formie wieloliniowej za pomocą klucza `implement`:

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

Przy zapisie tym dłuższym sposobem można podać dodatkowe argumenty dla konstruktora w kluczu `arguments` oraz dodatkową konfigurację za pomocą `setup`, tak samo jak w przypadku zwykłych usług.

Przykład: gdyby metoda `create()` nie przyjmowała parametru `$authorId`, moglibyśmy podać stałą wartość w konfiguracji, która byłaby przekazywana do konstruktora `Article`:

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

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

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


Accessor
========

Nette potrafi oprócz fabryk generować również tzw. akcesory. Są to obiekty z metodą `get()`, która zwraca określoną usługę z kontenera DI. Powtarzane wywołanie `get()` zwraca zawsze tę samą instancję.

Akcesory zapewniają lazy-loading zależności. Miejmy klasę, która zapisuje błędy do specjalnej bazy danych. Gdyby ta klasa otrzymywała połączenie z bazą danych jako zależność przez konstruktor, połączenie musiałoby być zawsze tworzone, chociaż w praktyce błąd pojawia się tylko wyjątkowo, a więc w większości przypadków połączenie pozostałoby niewykorzystane. Zamiast tego klasa przekaże sobie akcesor i dopiero gdy zostanie wywołana jego metoda `get()`, dojdzie do utworzenia obiektu bazy danych:

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

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

Akcesor dodajemy do pliku konfiguracyjnego, gdzie znajduje się również definicja usługi, którą będzie zwracał:

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

Ponieważ akcesor zwraca usługę typu `PDO`, a w konfiguracji jest jedyna taka usługa, będzie zwracał właśnie ją. Gdyby usług danego typu było więcej, określimy zwracaną usługę za pomocą nazwy, np. `- PDOAccessor(@db1)`.


Wielokrotna fabryka/akcesor
===========================
Nasze fabryki i akcesory potrafiły dotychczas zawsze tworzyć lub zwracać tylko jeden obiekt. Można jednak bardzo łatwo utworzyć również wielokrotne fabryki połączone z akcesorami. Interfejs takiej klasy będzie zawierał dowolną liczbę metod o nazwach `create<name>()` i `get<name>()`, np.:

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

Więc zamiast przekazywać sobie kilka generowanych fabryk i akcesorów, przekażemy jedną bardziej złożoną fabrykę, która potrafi więcej.

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

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

Wtedy obowiązuje, że `MultiFactory::getArticle()` robi to samo co `MultiFactoryAlt::get('article')`. Jednak alternatywny zapis ma tę wadę, że nie jest oczywiste, jakie wartości `$name` są obsługiwane i logicznie rzecz biorąc, nie można również w interfejsie rozróżnić różnych wartości zwracanych dla różnych `$name`.


Definicja listą
---------------
W ten sposób można zdefiniować wielokrotną fabrykę w konfiguracji: .{data-version:3.2.0}

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

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

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


Definicja za pomocą tagów
-------------------------

Drugą możliwością jest wykorzystanie do definicji [tagów |services#Tagi]:

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

Generowane fabryki

Nette DI potrafi automatycznie generować kod fabryk na podstawie interfejsów, co oszczędza Ci pisania kodu.

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

Jak wygląda taka fabryka, pokazaliśmy w rozdziale wstępnym:

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

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

Nette DI potrafi automatycznie generować kod fabryk. Wszystko, co musisz zrobić, to utworzyć interfejs, a Nette DI wygeneruje implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie create i deklarować typ zwracany:

interface ArticleFactory
{
	function create(): Article;
}

Czyli fabryka ArticleFactory ma metodę create, która tworzy obiekty Article. Klasa Article może wyglądać na przykład następująco:

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

Fabrykę dodajemy do pliku konfiguracyjnego:

services:
	- ArticleFactory

Nette DI wygeneruje odpowiednią implementację fabryki.

W kodzie, który używa fabryki, żądamy obiektu według interfejsu, a Nette DI użyje wygenerowanej implementacji:

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

	public function foo()
	{
		// zlecamy fabryce utworzenie obiektu
		$article = $this->articleFactory->create();
	}
}

Fabryka sparametryzowana

Metoda fabryczna create może przyjmować parametry, które następnie przekaże do konstruktora. Uzupełnijmy na przykład klasę Article o ID autora artykułu:

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

Parametr dodamy również do fabryki:

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

Dzięki temu, że parametr w konstruktorze i parametr w fabryce nazywają się tak samo, Nette DI przekaże je całkowicie automatycznie.

Definicja zaawansowana

Definicję można zapisać również w formie wieloliniowej za pomocą klucza implement:

services:
	articleFactory:
		implement: ArticleFactory

Przy zapisie tym dłuższym sposobem można podać dodatkowe argumenty dla konstruktora w kluczu arguments oraz dodatkową konfigurację za pomocą setup, tak samo jak w przypadku zwykłych usług.

Przykład: gdyby metoda create() nie przyjmowała parametru $authorId, moglibyśmy podać stałą wartość w konfiguracji, która byłaby przekazywana do konstruktora Article:

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

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

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

Accessor

Nette potrafi oprócz fabryk generować również tzw. akcesory. Są to obiekty z metodą get(), która zwraca określoną usługę z kontenera DI. Powtarzane wywołanie get() zwraca zawsze tę samą instancję.

Akcesory zapewniają lazy-loading zależności. Miejmy klasę, która zapisuje błędy do specjalnej bazy danych. Gdyby ta klasa otrzymywała połączenie z bazą danych jako zależność przez konstruktor, połączenie musiałoby być zawsze tworzone, chociaż w praktyce błąd pojawia się tylko wyjątkowo, a więc w większości przypadków połączenie pozostałoby niewykorzystane. Zamiast tego klasa przekaże sobie akcesor i dopiero gdy zostanie wywołana jego metoda get(), dojdzie do utworzenia obiektu bazy danych:

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

interface PDOAccessor
{
	function get(): PDO;
}

Akcesor dodajemy do pliku konfiguracyjnego, gdzie znajduje się również definicja usługi, którą będzie zwracał:

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

Ponieważ akcesor zwraca usługę typu PDO, a w konfiguracji jest jedyna taka usługa, będzie zwracał właśnie ją. Gdyby usług danego typu było więcej, określimy zwracaną usługę za pomocą nazwy, np. - PDOAccessor(@db1).

Wielokrotna fabryka/akcesor

Nasze fabryki i akcesory potrafiły dotychczas zawsze tworzyć lub zwracać tylko jeden obiekt. Można jednak bardzo łatwo utworzyć również wielokrotne fabryki połączone z akcesorami. Interfejs takiej klasy będzie zawierał dowolną liczbę metod o nazwach create<name>() i get<name>(), np.:

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

Więc zamiast przekazywać sobie kilka generowanych fabryk i akcesorów, przekażemy jedną bardziej złożoną fabrykę, która potrafi więcej.

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

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

Wtedy obowiązuje, że MultiFactory::getArticle() robi to samo co MultiFactoryAlt::get('article'). Jednak alternatywny zapis ma tę wadę, że nie jest oczywiste, jakie wartości $name są obsługiwane i logicznie rzecz biorąc, nie można również w interfejsie rozróżnić różnych wartości zwracanych dla różnych $name.

Definicja listą

W ten sposób można zdefiniować wielokrotną fabrykę w konfiguracji:

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

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

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

Definicja za pomocą tagów

Drugą możliwością jest wykorzystanie do definicji tagów:

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