Nette Documentation Preview

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

.[perex]
Nette DI potrafi automatycznie wygenerować kod fabryki na podstawie interfejsów, oszczędzając Ci pisania kodu.

Fabryka to klasa odpowiedzialna za tworzenie obiektów i przekazywanie ich zależności. Nie myl tego ze wzorcem projektowym *factory method*, który opisuje konkretny sposób używania fabryk i nie ma z tym tematem związku.

Jak taka fabryka wygląda, pokazaliśmy w [rozdziale wprowadzającym |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 wygenerować kod fabryki. Wystarczy, że utworzysz 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;
}
```

Fabryka `ArticleFactory` ma więc 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 odpowiadającą jej implementację fabryki.

W kodzie używającym fabryki poproś o obiekt przez jego interfejs, a Nette DI dostarczy wygenerowaną implementację:

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

	public function foo()
	{
		// pozwalamy fabryce utworzyć obiekt
		$article = $this->articleFactory->create();
	}
}
```


Fabryka z parametrami
=====================

Metoda fabryczna `create` może przyjmować parametry, które następnie przekazuje do konstruktora. Dodajmy na przykład do klasy `Article` 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;
}
```

Ponieważ nazwa parametru w konstruktorze (`$authorId`) zgadza się z nazwą parametru w metodzie fabrycznej, Nette DI przekazuje go automatycznie.


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

Definicję można zapisać również w postaci wielowierszowej, używając klucza `implement`:

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

Użycie tej dłuższej postaci pozwala podać dodatkowe argumenty konstruktora kluczem `arguments` i dalszą konfigurację przez `setup`, podobnie jak przy zwykłych definicjach usług.

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

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

Odwrotnie, gdyby `create()` przyjmowało `$authorId`, ale nie byłby on częścią konstruktora, tylko przekazywany metodą w rodzaju `Article::setAuthorId()`, odwołalibyśmy się do parametru w sekcji `setup`:

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


Akcesor
=======

Poza fabrykami Nette potrafi generować również tak zwane akcesory. To obiekty z metodą `get()`, która zwraca konkretną usługę z kontenera DI. Powtarzane wywołania `get()` zawsze zwracają tę samą instancję.

Akcesory zapewniają leniwe ładowanie zależności. Rozważ klasę, która loguje błędy do dedykowanej bazy danych. Gdyby klasa ta otrzymywała połączenie z bazą danych przez wstrzykiwanie w konstruktorze, połączenie nawiązywałoby się zawsze, nawet jeśli błędy występują rzadko, a połączenie przez większość czasu pozostaje niewykorzystane. Zamiast tego klasa może otrzymać akcesor. Obiekt bazy danych (połączenie) tworzony jest dopiero wtedy, gdy metoda `get()` akcesora zostanie wywołana po raz pierwszy.

Jak utworzyć akcesor? Wystarczy napisać interfejs, a Nette DI wygeneruje implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie `get`, która nie przyjmuje parametrów i deklaruje typ zwracany:

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

Dodaj akcesor do pliku konfiguracyjnego wraz z definicją usługi, którą ma zwracać:

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

Ponieważ akcesor zwraca usługę `PDO`, a w konfiguracji zdefiniowana jest tylko jedna taka usługa, akcesor zwróci właśnie ją. Jeśli usług tego typu jest więcej, podaj po nazwie, którą akcesor ma zwracać, np. `- PDOAccessor(@db1)`.


Multifabryka/akcesor
====================

Jak dotąd nasze fabryki i akcesory potrafiły tworzyć albo zwracać tylko jeden typ obiektu. Możesz jednak łatwo utworzyć multifabryki, które łączą cechy fabryk i akcesorów. Interfejs takiego komponentu może zawierać wiele metod o nazwach `create<Nazwa>()` i `get<Nazwa>()`, na przykład:

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

Zamiast wstrzykiwać wiele osobnych fabryk i akcesorów, możesz więc wstrzyknąć jeden, bardziej kompleksowy komponent.

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

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

`MultiFactory::getDb()` robi wtedy to samo co `MultiFactoryAlt::get('db')`. Ten alternatywny zapis ma jednak tę wadę, że obsługiwane wartości `$name` nie wynikają jawnie z sygnatury interfejsu. Ponadto nie da się zdefiniować w interfejsie różnych typów zwracanych dla różnych wartości `$name`.

Zamiast `get($name)` interfejs może deklarować `create($name)`, który przy każdym wywołaniu zwraca nową instancję (podczas gdy `get()` zwraca współdzieloną). Interfejs może zawierać tylko jedną taką metodę z parametrem. Jeśli typ zwracany metody jest nullable (np. `?PDO`), dla nieznanego `$name` zwraca `null` zamiast zgłaszać wyjątek.


Definicja z listą
-----------------
Multifabrykę możesz zdefiniować w konfiguracji za pomocą listy, z usługami zapisanymi w linii: .{data-version:3.2.0}

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

Alternatywnie możesz odwołać się w definicji multifabryki 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 z tagami
------------------

Innym sposobem zdefiniowania multifabryki jest użycie [tagów |services#Tagi]. Wartość tagu określa nazwę odpowiadającej metody:

```neon
services:
	article:
		create: Article
		tags: {multi: article}     # definiuje createArticle()
	db:
		create: PDO(%dsn%, %user%, %password%)
		tags: {multi: db}          # definiuje getDb()

	- MultiFactory(tagged: multi)
```

Generowane fabryki

Nette DI potrafi automatycznie wygenerować kod fabryki na podstawie interfejsów, oszczędzając Ci pisania kodu.

Fabryka to klasa odpowiedzialna za tworzenie obiektów i przekazywanie ich zależności. Nie myl tego ze wzorcem projektowym factory method, który opisuje konkretny sposób używania fabryk i nie ma z tym tematem związku.

Jak taka fabryka wygląda, 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 potrafi automatycznie wygenerować kod fabryki. Wystarczy, że utworzysz 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;
}

Fabryka ArticleFactory ma więc 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 odpowiadającą jej implementację fabryki.

W kodzie używającym fabryki poproś o obiekt przez jego interfejs, a Nette DI dostarczy wygenerowaną implementację:

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

	public function foo()
	{
		// pozwalamy fabryce utworzyć obiekt
		$article = $this->articleFactory->create();
	}
}

Fabryka z parametrami

Metoda fabryczna create może przyjmować parametry, które następnie przekazuje do konstruktora. Dodajmy na przykład do klasy Article 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;
}

Ponieważ nazwa parametru w konstruktorze ($authorId) zgadza się z nazwą parametru w metodzie fabrycznej, Nette DI przekazuje go automatycznie.

Definicja zaawansowana

Definicję można zapisać również w postaci wielowierszowej, używając klucza implement:

services:
	articleFactory:
		implement: ArticleFactory

Użycie tej dłuższej postaci pozwala podać dodatkowe argumenty konstruktora kluczem arguments i dalszą konfigurację przez setup, podobnie jak przy zwykłych definicjach usług.

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

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

Odwrotnie, gdyby create() przyjmowało $authorId, ale nie byłby on częścią konstruktora, tylko przekazywany metodą w rodzaju Article::setAuthorId(), odwołalibyśmy się do parametru w sekcji setup:

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

Akcesor

Poza fabrykami Nette potrafi generować również tak zwane akcesory. To obiekty z metodą get(), która zwraca konkretną usługę z kontenera DI. Powtarzane wywołania get() zawsze zwracają tę samą instancję.

Akcesory zapewniają leniwe ładowanie zależności. Rozważ klasę, która loguje błędy do dedykowanej bazy danych. Gdyby klasa ta otrzymywała połączenie z bazą danych przez wstrzykiwanie w konstruktorze, połączenie nawiązywałoby się zawsze, nawet jeśli błędy występują rzadko, a połączenie przez większość czasu pozostaje niewykorzystane. Zamiast tego klasa może otrzymać akcesor. Obiekt bazy danych (połączenie) tworzony jest dopiero wtedy, gdy metoda get() akcesora zostanie wywołana po raz pierwszy.

Jak utworzyć akcesor? Wystarczy napisać interfejs, a Nette DI wygeneruje implementację. Interfejs musi mieć dokładnie jedną metodę o nazwie get, która nie przyjmuje parametrów i deklaruje typ zwracany:

interface PDOAccessor
{
	function get(): PDO;
}

Dodaj akcesor do pliku konfiguracyjnego wraz z definicją usługi, którą ma zwracać:

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

Ponieważ akcesor zwraca usługę PDO, a w konfiguracji zdefiniowana jest tylko jedna taka usługa, akcesor zwróci właśnie ją. Jeśli usług tego typu jest więcej, podaj po nazwie, którą akcesor ma zwracać, np. - PDOAccessor(@db1).

Multifabryka/akcesor

Jak dotąd nasze fabryki i akcesory potrafiły tworzyć albo zwracać tylko jeden typ obiektu. Możesz jednak łatwo utworzyć multifabryki, które łączą cechy fabryk i akcesorów. Interfejs takiego komponentu może zawierać wiele metod o nazwach create<Nazwa>() i get<Nazwa>(), na przykład:

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

Zamiast wstrzykiwać wiele osobnych fabryk i akcesorów, możesz więc wstrzyknąć jeden, bardziej kompleksowy komponent.

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

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

MultiFactory::getDb() robi wtedy to samo co MultiFactoryAlt::get('db'). Ten alternatywny zapis ma jednak tę wadę, że obsługiwane wartości $name nie wynikają jawnie z sygnatury interfejsu. Ponadto nie da się zdefiniować w interfejsie różnych typów zwracanych dla różnych wartości $name.

Zamiast get($name) interfejs może deklarować create($name), który przy każdym wywołaniu zwraca nową instancję (podczas gdy get() zwraca współdzieloną). Interfejs może zawierać tylko jedną taką metodę z parametrem. Jeśli typ zwracany metody jest nullable (np. ?PDO), dla nieznanego $name zwraca null zamiast zgłaszać wyjątek.

Definicja z listą

Multifabrykę możesz zdefiniować w konfiguracji za pomocą listy, z usługami zapisanymi w linii:

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

Alternatywnie możesz odwołać się w definicji multifabryki 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 z tagami

Innym sposobem zdefiniowania multifabryki jest użycie tagów. Wartość tagu określa nazwę odpowiadającej metody:

services:
	article:
		create: Article
		tags: {multi: article}     # definiuje createArticle()
	db:
		create: PDO(%dsn%, %user%, %password%)
		tags: {multi: db}          # definiuje getDb()

	- MultiFactory(tagged: multi)