Nette Documentation Preview

syntax
Generirane tovarne
******************

.[perex]
Nette DI lahko na podlagi vmesnika samodejno ustvari kodo tovarne, kar vam prihrani pisanje kode.

Tovarna je razred, ki ustvarja in konfigurira predmete. Zato jim posreduje tudi njihove odvisnosti. Ne zamenjujte z oblikovnim vzorcem *tovarovna metoda*, ki opisuje poseben način uporabe tovarn in ni povezan s to temo.

V [uvodnem poglavju |introduction#factory] smo pokazali, kako je videti takšna tovarna:

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

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

Nette DI lahko samodejno ustvari tovarniško kodo. Vse, kar morate storiti, je, da ustvarite vmesnik in Nette DI bo ustvaril implementacijo. Vmesnik mora imeti natančno eno metodo z imenom `create` in deklarirati tip vrnitve:

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

Tako ima tovarna `ArticleFactory` metodo `create`, ki ustvari predmete `Article`. Razred `Article` je lahko na primer videti takole:

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

Dodajte tovarno v konfiguracijsko datoteko:

```neon
services:
	- ArticleFactory
```

Nette DI bo ustvaril ustrezno implementacijo tovarne.

Tako v kodi, ki uporablja tovarno, zahtevamo objekt po vmesniku, Nette DI pa uporabi generirano implementacijo:

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

	public function foo()
	{
		// naj tovarna ustvari predmet
		$article = $this->articleFactory->create();
	}
}
```


Parametrizirana tovarna .[#toc-parameterized-factory]
=====================================================

Tovarniška metoda `create` lahko sprejme parametre, ki jih nato posreduje konstruktorju. Na primer, dodajmo ID avtorja članka v razred `Article`:

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

Parameter bomo dodali tudi tovarni:

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

Ker imata parameter v konstruktorju in parameter v tovarni enako ime, ju bo Nette DI samodejno posredoval.


Napredna definicija .[#toc-advanced-definition]
===============================================

Definicijo lahko zapišete tudi v večvrstični obliki z uporabo tipke `implement`:

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

Pri tem daljšem zapisu je mogoče navesti dodatne argumente za konstruktor v ključu `arguments` in dodatno konfiguracijo s ključem `setup`, tako kot pri običajnih storitvah.

Primer: če metoda `create()` ne bi sprejela parametra `$authorId`, bi lahko v konfiguraciji določili fiksno vrednost, ki bi se posredovala konstruktorju `Article`:

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

Ali obratno, če bi `create()` sprejel parameter `$authorId`, vendar ta ne bi bil del konstruktorja in bi ga posredovala metoda `Article::setAuthorId()`, bi se nanj sklicevali v razdelku `setup`:

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


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

Poleg tovarn lahko Nette ustvari tudi tako imenovane dostopnike. Accessor je objekt z metodo `get()`, ki vrača določeno storitev iz vsebnika DI. Več klicev `get()` bo vedno vrnilo isto instanco.

Dostopi prinašajo lenobno nalaganje v odvisnosti. Imejmo razred, ki beleži napake v posebno podatkovno zbirko. Če bi bila povezava s podatkovno bazo posredovana kot odvisnost v njegovem konstruktorju, bi bilo treba povezavo vedno ustvariti, čeprav bi jo uporabili le redko, ko se pojavi napaka, zato bi povezava ostala večinoma neuporabljena.
Namesto tega lahko razred posreduje dostopnik in ko se pokliče njegova metoda `get()`, se šele takrat ustvari objekt podatkovne zbirke:

Kako ustvariti accessor? Napišite samo vmesnik in Nette DI bo ustvaril implementacijo. Vmesnik mora imeti natanko eno metodo z imenom `get` in mora deklarirati tip vrnitve:

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

Dostopnik dodajte v konfiguracijsko datoteko skupaj z definicijo storitve, ki jo bo dostopnik vrnil:

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

 `PDO` Ker je v konfiguraciji samo ena takšna storitev, jo bo vrnil. Pri več konfiguriranih storitvah te vrste lahko določite, katera naj se vrne, z uporabo njenega imena, na primer `- PDOAccessor(@db1)`.


Multifactory/Accessor .[#toc-multifactory-accessor]
===================================================
Do zdaj so lahko tovarne in dostopniki ustvarili ali vrnili samo en predmet. Ustvarimo lahko tudi večfazo v kombinaciji z dostopnikom. Vmesnik takega razreda multifactory je lahko sestavljen iz več metod, imenovanih `create<name>()` in `get<name>()`na primer:

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

Namesto več ustvarjenih tovarn in dostopov lahko posredujete samo eno kompleksno večfazno tovarno.

Namesto več metod lahko uporabite tudi `get()` s parametrom:

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

V tem primeru `MultiFactory::getArticle()` naredi isto kot `MultiFactoryAlt::get('article')`. Vendar ima alternativna sintaksa nekaj pomanjkljivosti. Ni jasno, katere vrednosti `$name` so podprte, v vmesniku pa ni mogoče določiti tipa vrnitve pri uporabi več različnih vrednosti `$name`.


Opredelitev s seznamom .[#toc-definition-with-a-list]
-----------------------------------------------------
Na ta način lahko v konfiguraciji določite več tovarn: .{data-version:3.2.0}

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

Lahko pa se v definiciji tovarne sklicujemo na obstoječe storitve s pomočjo reference:

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


Opredelitev z oznakami .[#toc-definition-with-tags]
---------------------------------------------------

Druga možnost, kako opredeliti večpredstavnostno zbirko, je uporaba [oznak |services#Tags]:

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

Generirane tovarne

Nette DI lahko na podlagi vmesnika samodejno ustvari kodo tovarne, kar vam prihrani pisanje kode.

Tovarna je razred, ki ustvarja in konfigurira predmete. Zato jim posreduje tudi njihove odvisnosti. Ne zamenjujte z oblikovnim vzorcem tovarovna metoda, ki opisuje poseben način uporabe tovarn in ni povezan s to temo.

uvodnem poglavju smo pokazali, kako je videti takšna tovarna:

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

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

Nette DI lahko samodejno ustvari tovarniško kodo. Vse, kar morate storiti, je, da ustvarite vmesnik in Nette DI bo ustvaril implementacijo. Vmesnik mora imeti natančno eno metodo z imenom create in deklarirati tip vrnitve:

interface ArticleFactory
{
	function create(): Article;
}

Tako ima tovarna ArticleFactory metodo create, ki ustvari predmete Article. Razred Article je lahko na primer videti takole:

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

Dodajte tovarno v konfiguracijsko datoteko:

services:
	- ArticleFactory

Nette DI bo ustvaril ustrezno implementacijo tovarne.

Tako v kodi, ki uporablja tovarno, zahtevamo objekt po vmesniku, Nette DI pa uporabi generirano implementacijo:

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

	public function foo()
	{
		// naj tovarna ustvari predmet
		$article = $this->articleFactory->create();
	}
}

Parametrizirana tovarna

Tovarniška metoda create lahko sprejme parametre, ki jih nato posreduje konstruktorju. Na primer, dodajmo ID avtorja članka v razred Article:

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

Parameter bomo dodali tudi tovarni:

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

Ker imata parameter v konstruktorju in parameter v tovarni enako ime, ju bo Nette DI samodejno posredoval.

Napredna definicija

Definicijo lahko zapišete tudi v večvrstični obliki z uporabo tipke implement:

services:
	articleFactory:
		implement: ArticleFactory

Pri tem daljšem zapisu je mogoče navesti dodatne argumente za konstruktor v ključu arguments in dodatno konfiguracijo s ključem setup, tako kot pri običajnih storitvah.

Primer: če metoda create() ne bi sprejela parametra $authorId, bi lahko v konfiguraciji določili fiksno vrednost, ki bi se posredovala konstruktorju Article:

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

Ali obratno, če bi create() sprejel parameter $authorId, vendar ta ne bi bil del konstruktorja in bi ga posredovala metoda Article::setAuthorId(), bi se nanj sklicevali v razdelku setup:

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

Accessor

Poleg tovarn lahko Nette ustvari tudi tako imenovane dostopnike. Accessor je objekt z metodo get(), ki vrača določeno storitev iz vsebnika DI. Več klicev get() bo vedno vrnilo isto instanco.

Dostopi prinašajo lenobno nalaganje v odvisnosti. Imejmo razred, ki beleži napake v posebno podatkovno zbirko. Če bi bila povezava s podatkovno bazo posredovana kot odvisnost v njegovem konstruktorju, bi bilo treba povezavo vedno ustvariti, čeprav bi jo uporabili le redko, ko se pojavi napaka, zato bi povezava ostala večinoma neuporabljena. Namesto tega lahko razred posreduje dostopnik in ko se pokliče njegova metoda get(), se šele takrat ustvari objekt podatkovne zbirke:

Kako ustvariti accessor? Napišite samo vmesnik in Nette DI bo ustvaril implementacijo. Vmesnik mora imeti natanko eno metodo z imenom get in mora deklarirati tip vrnitve:

interface PDOAccessor
{
	function get(): PDO;
}

Dostopnik dodajte v konfiguracijsko datoteko skupaj z definicijo storitve, ki jo bo dostopnik vrnil:

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

PDO Ker je v konfiguraciji samo ena takšna storitev, jo bo vrnil. Pri več konfiguriranih storitvah te vrste lahko določite, katera naj se vrne, z uporabo njenega imena, na primer - PDOAccessor(@db1).

Multifactory/Accessor

Do zdaj so lahko tovarne in dostopniki ustvarili ali vrnili samo en predmet. Ustvarimo lahko tudi večfazo v kombinaciji z dostopnikom. Vmesnik takega razreda multifactory je lahko sestavljen iz več metod, imenovanih create<name>() in get<name>()na primer:

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

Namesto več ustvarjenih tovarn in dostopov lahko posredujete samo eno kompleksno večfazno tovarno.

Namesto več metod lahko uporabite tudi get() s parametrom:

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

V tem primeru MultiFactory::getArticle() naredi isto kot MultiFactoryAlt::get('article'). Vendar ima alternativna sintaksa nekaj pomanjkljivosti. Ni jasno, katere vrednosti $name so podprte, v vmesniku pa ni mogoče določiti tipa vrnitve pri uporabi več različnih vrednosti $name.

Opredelitev s seznamom

Na ta način lahko v konfiguraciji določite več tovarn:

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

Lahko pa se v definiciji tovarne sklicujemo na obstoječe storitve s pomočjo reference:

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

Opredelitev z oznakami

Druga možnost, kako opredeliti večpredstavnostno zbirko, je uporaba oznak:

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