Nette Documentation Preview

syntax
Autowiring
**********

.[perex]
Autowiring je skvělá vlastnost, která umí automaticky předávat do konstruktoru a dalších metod požadované služby, takže je nemusíme vůbec psát. Ušetří vám spoustu času.

Díky tomu můžeme vynechat naprostou většinu argumentů při psaní definic služeb. Místo:

```neon
services:
	articles: Model\ArticleRepository(@database, @cache.storage)
```

Stačí napsat:

```neon
services:
	articles: Model\ArticleRepository
```

Autowiring se řídí podle typů, takže aby fungoval, musí být třída `ArticleRepository` definována asi takto:

```php
namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}
```

Autowiring nikdy nepoužívá názvy služeb. Řídí se výhradně typovým systémem PHP, takže ví i to, že třída vyhovuje rozhraním, která implementuje, a třídám, ze kterých dědí. Díky tomu je název služby jen pomocný identifikátor a jeho přejmenování v aplikaci nic nerozbije.

Aby bylo možné použít autowiring, musí pro každý typ být v kontejneru **právě jedna služba**. Pokud by jich bylo víc, autowiring by nevěděl, kterou z nich předat, a vyhodil by výjimku:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # VYHODÍ VÝJIMKU, vyhovuje mainDb i tempDb
```

Řešením by bylo autowiring obejít a explicitně uvést název služby (např. `articles: Model\ArticleRepository(@mainDb)`). Šikovnější ale je autowirování jedné ze služeb [vypnout |#Vypnutí autowiringu], nebo jednu ze služeb [upřednostnit |#Preference autowiringu].


Vypnutí autowiringu
-------------------

Autowirování služby můžeme vypnout pomocí volby `autowired: false`:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # služba tempDb je vyřazena z autowiringu

	articles: Model\ArticleRepository  # tudíž předá do konstruktoru mainDb
```

Služba `articles` nevyhodí výjimku, že existují dvě vyhovující služby typu `PDO` (tj. `mainDb` a `tempDb`), které lze do konstruktoru předat, protože vidí jen službu `mainDb`.

Autowiring lze také globálně vypnout pro celé typy pomocí konfigurační volby [`di › excluded` |configuration#DI], která vyjmenovává typy (a jejich potomky), které se nikdy nemají autowirovat.

.[note]
Konfigurace autowiringu v Nette funguje odlišně než v Symfony, kde volba `autowire: false` říká, že se nemá autowiring používat pro argumenty konstruktoru dané služby. V Nette se autowiring používá vždy, ať už pro argumenty konstruktoru, nebo kterékoliv jiné metody. Volba `autowired: false` říká, že instance dané služby nemá být pomocí autowiringu nikam předávána.


Preference autowiringu
----------------------

Pokud máme více služeb stejného typu a u jedné z nich uvedeme volbu `autowired`, stává se tato služba preferovanou:

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # stává se preferovanou

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository
```

Služba `articles` nevyhodí výjimku, že existují dvě vyhovující služby typu `PDO` (tj. `mainDb` a `tempDb`), ale použije preferovanou službu, tedy `mainDb`.


Pole služeb
-----------

Autowiring umí předávat i pole služeb určitého typu. Protože v PHP nelze nativně zapsat typ položek pole, je třeba kromě typu `array` doplnit i phpDoc komentář s typem položky ve tvaru `ClassName[]`:

```php
namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}
```

DI kontejner pak automaticky předá pole služeb odpovídajících danému typu. Vynechá služby, které mají [vypnutý autowiring |#Vypnutí autowiringu], a nikdy do kolekce nezahrne službu, která se právě vytváří. Na rozdíl od předávání jednotlivé služby zde [zúžení |#Zúžení autowiringu] autowiringu na konkrétní typ ani označení služby jako [preferované |#Preference autowiringu] nehraje roli; pole vždy obsahuje všechny služby daného typu.

Typ v komentáři může být také ve tvaru `array<int, Class>` nebo `list<Class>`. Pokud nemůžete ovlivnit podobu phpDoc komentáře, můžete předat pole služeb přímo v konfiguraci pomocí [`typed()` |services#Speciální funkce].


Skalární argumenty
------------------

Autowiring umí dosazovat pouze objekty a pole objektů. Skalární argumenty (např. řetězce, čísla, booleany) [zapíšeme v konfiguraci |services#Argumenty]. Alternativou je vytvořit [settings-objekt |best-practices:passing-settings-to-presenters], který skalární hodnotu (nebo více hodnot) zapouzdří do podoby objektu, a ten pak lze opět předávat pomocí autowiringu.

```php
class MySettings
{
	public function __construct(
		// readonly je možné použít od PHP 8.1
		public readonly bool $value,
	)
	{}
}
```

Vytvoříte z něj službu přidáním do konfigurace:

```neon
services:
	- MySettings('any value')
```

Ostatní třídy si jej pak mohou vyžádat pomocí autowiringu.


Volitelné závislosti
--------------------

Pokud má parametr konstruktoru nebo metody výchozí hodnotu a v kontejneru neexistuje žádná služba požadovaného typu, autowiring nevyhodí výjimku, ale argument jednoduše vynechá, takže se použije výchozí hodnota. Takto deklarujete volitelné závislosti:

```php
class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}
```

Naproti tomu u parametru bez výchozí hodnoty chybějící služba vždy způsobí výjimku.


Zúžení autowiringu
------------------

Jednotlivým službám lze autowiring zúžit jen na určité třídy nebo rozhraní.

Normálně autowiring službu předá do každého parametru metody, jehož typu služba odpovídá. Zúžení znamená, že stanovíme podmínky, kterým musí typy uvedené u parametrů metod vyhovovat, aby jim byla služba předána.

Ukážeme si to na příkladu:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Pokud bychom je všechny zaregistrovali jako služby, tak by autowiring selhal:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # VYHODÍ VÝJIMKU, vyhovují služby parent i child
	childDep: ChildDependent    # autowiring předá do konstruktoru službu child
```

Služba `parentDep` vyhodí výjimku `Multiple services of type ParentClass found: child, parent`, protože do jejího konstruktoru pasují obě služby `parent` i `child`, a autowiring nemůže rozhodnout, kterou z nich zvolit.

U služby `child` můžeme proto zúžit její autowirování na typ `ChildClass`:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # lze napsat i 'autowired: self'

	parentDep: ParentDependent  # autowiring předá do konstruktoru službu parent
	childDep: ChildDependent    # autowiring předá do konstruktoru službu child
```

Nyní se do konstruktoru služby `parentDep` předá služba `parent`, protože teď je to jediný vyhovující objekt. Službu `child` už tam autowiring nepředá. Ano, služba `child` je stále typu `ParentClass`, ale zužující podmínka `autowired: ChildClass` znamená, že se předá jen do parametrů explicitně typovaných jako `ChildClass` (nebo jeho podtypy). Protože `ParentDependent` vyžaduje `ParentClass`, služba `child` už tam pro autowiring nepřipadá v úvahu.

U služby `child` by bylo možné `autowired: ChildClass` zapsat také jako `autowired: self`, jelikož `self` je zástupné označení pro třídu aktuální služby.

V klíči `autowired` je možné uvést i několik tříd nebo interfaců jako pole:

```neon
autowired: [ParentClass, FooInterface]
```

Zkusme příklad doplnit ještě o rozhraní:

```php
interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Když službu `child` nijak neomezíme, bude pasovat do konstruktorů všech tříd `FooDependent`, `BarDependent`, `ParentDependent` i `ChildDependent` a autowiring ji tam předá.

Pokud její autowiring ale zúžíme na `ChildClass` pomocí `autowired: ChildClass` (nebo `self`), předá ji autowiring pouze do konstruktoru `ChildDependent`, protože vyžaduje argument typu `ChildClass` a platí, že `ChildClass` *je typu* `ChildClass`. Žádný z požadovaných typů ostatních parametrů není `ChildClass` ani jeho podtyp, takže se jim služba nepředá.

Pokud jej omezíme na `ParentClass` pomocí `autowired: ParentClass`, předá ji autowiring opět do konstruktoru `ChildDependent` (protože vyžadovaný `ChildClass` je podtyp `ParentClass`) a nově i do konstruktoru `ParentDependent`, protože vyžadovaný typ `ParentClass` je taktéž vyhovující.

Pokud jej omezíme na `FooInterface`, bude stále autowirovaná do `ParentDependent` (vyžadovaný `ParentClass` je podtyp `FooInterface`) a `ChildDependent`, a navíc i do konstruktoru `FooDependent`, nikoliv však do `BarDependent`, neboť `BarInterface` není podtyp `FooInterface`.

```neon
services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # autowiring předá do konstruktoru child
	barDep: BarDependent        # VYHODÍ VÝJIMKU, žádná služba nevyhovuje
	parentDep: ParentDependent  # autowiring předá do konstruktoru child
	childDep: ChildDependent    # autowiring předá do konstruktoru child
```

Autowiring

Autowiring je skvělá vlastnost, která umí automaticky předávat do konstruktoru a dalších metod požadované služby, takže je nemusíme vůbec psát. Ušetří vám spoustu času.

Díky tomu můžeme vynechat naprostou většinu argumentů při psaní definic služeb. Místo:

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

Stačí napsat:

services:
	articles: Model\ArticleRepository

Autowiring se řídí podle typů, takže aby fungoval, musí být třída ArticleRepository definována asi takto:

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

Autowiring nikdy nepoužívá názvy služeb. Řídí se výhradně typovým systémem PHP, takže ví i to, že třída vyhovuje rozhraním, která implementuje, a třídám, ze kterých dědí. Díky tomu je název služby jen pomocný identifikátor a jeho přejmenování v aplikaci nic nerozbije.

Aby bylo možné použít autowiring, musí pro každý typ být v kontejneru právě jedna služba. Pokud by jich bylo víc, autowiring by nevěděl, kterou z nich předat, a vyhodil by výjimku:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # VYHODÍ VÝJIMKU, vyhovuje mainDb i tempDb

Řešením by bylo autowiring obejít a explicitně uvést název služby (např. articles: Model\ArticleRepository(@mainDb)). Šikovnější ale je autowirování jedné ze služeb vypnout, nebo jednu ze služeb upřednostnit.

Vypnutí autowiringu

Autowirování služby můžeme vypnout pomocí volby autowired: false:

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # služba tempDb je vyřazena z autowiringu

	articles: Model\ArticleRepository  # tudíž předá do konstruktoru mainDb

Služba articles nevyhodí výjimku, že existují dvě vyhovující služby typu PDO (tj. mainDb a tempDb), které lze do konstruktoru předat, protože vidí jen službu mainDb.

Autowiring lze také globálně vypnout pro celé typy pomocí konfigurační volby di › excluded, která vyjmenovává typy (a jejich potomky), které se nikdy nemají autowirovat.

Konfigurace autowiringu v Nette funguje odlišně než v Symfony, kde volba autowire: false říká, že se nemá autowiring používat pro argumenty konstruktoru dané služby. V Nette se autowiring používá vždy, ať už pro argumenty konstruktoru, nebo kterékoliv jiné metody. Volba autowired: false říká, že instance dané služby nemá být pomocí autowiringu nikam předávána.

Preference autowiringu

Pokud máme více služeb stejného typu a u jedné z nich uvedeme volbu autowired, stává se tato služba preferovanou:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # stává se preferovanou

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

Služba articles nevyhodí výjimku, že existují dvě vyhovující služby typu PDO (tj. mainDb a tempDb), ale použije preferovanou službu, tedy mainDb.

Pole služeb

Autowiring umí předávat i pole služeb určitého typu. Protože v PHP nelze nativně zapsat typ položek pole, je třeba kromě typu array doplnit i phpDoc komentář s typem položky ve tvaru ClassName[]:

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

DI kontejner pak automaticky předá pole služeb odpovídajících danému typu. Vynechá služby, které mají vypnutý autowiring, a nikdy do kolekce nezahrne službu, která se právě vytváří. Na rozdíl od předávání jednotlivé služby zde zúžení autowiringu na konkrétní typ ani označení služby jako preferované nehraje roli; pole vždy obsahuje všechny služby daného typu.

Typ v komentáři může být také ve tvaru array<int, Class> nebo list<Class>. Pokud nemůžete ovlivnit podobu phpDoc komentáře, můžete předat pole služeb přímo v konfiguraci pomocí typed().

Skalární argumenty

Autowiring umí dosazovat pouze objekty a pole objektů. Skalární argumenty (např. řetězce, čísla, booleany) zapíšeme v konfiguraci. Alternativou je vytvořit settings-objekt, který skalární hodnotu (nebo více hodnot) zapouzdří do podoby objektu, a ten pak lze opět předávat pomocí autowiringu.

class MySettings
{
	public function __construct(
		// readonly je možné použít od PHP 8.1
		public readonly bool $value,
	)
	{}
}

Vytvoříte z něj službu přidáním do konfigurace:

services:
	- MySettings('any value')

Ostatní třídy si jej pak mohou vyžádat pomocí autowiringu.

Volitelné závislosti

Pokud má parametr konstruktoru nebo metody výchozí hodnotu a v kontejneru neexistuje žádná služba požadovaného typu, autowiring nevyhodí výjimku, ale argument jednoduše vynechá, takže se použije výchozí hodnota. Takto deklarujete volitelné závislosti:

class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}

Naproti tomu u parametru bez výchozí hodnoty chybějící služba vždy způsobí výjimku.

Zúžení autowiringu

Jednotlivým službám lze autowiring zúžit jen na určité třídy nebo rozhraní.

Normálně autowiring službu předá do každého parametru metody, jehož typu služba odpovídá. Zúžení znamená, že stanovíme podmínky, kterým musí typy uvedené u parametrů metod vyhovovat, aby jim byla služba předána.

Ukážeme si to na příkladu:

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Pokud bychom je všechny zaregistrovali jako služby, tak by autowiring selhal:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # VYHODÍ VÝJIMKU, vyhovují služby parent i child
	childDep: ChildDependent    # autowiring předá do konstruktoru službu child

Služba parentDep vyhodí výjimku Multiple services of type ParentClass found: child, parent, protože do jejího konstruktoru pasují obě služby parent i child, a autowiring nemůže rozhodnout, kterou z nich zvolit.

U služby child můžeme proto zúžit její autowirování na typ ChildClass:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # lze napsat i 'autowired: self'

	parentDep: ParentDependent  # autowiring předá do konstruktoru službu parent
	childDep: ChildDependent    # autowiring předá do konstruktoru službu child

Nyní se do konstruktoru služby parentDep předá služba parent, protože teď je to jediný vyhovující objekt. Službu child už tam autowiring nepředá. Ano, služba child je stále typu ParentClass, ale zužující podmínka autowired: ChildClass znamená, že se předá jen do parametrů explicitně typovaných jako ChildClass (nebo jeho podtypy). Protože ParentDependent vyžaduje ParentClass, služba child už tam pro autowiring nepřipadá v úvahu.

U služby child by bylo možné autowired: ChildClass zapsat také jako autowired: self, jelikož self je zástupné označení pro třídu aktuální služby.

V klíči autowired je možné uvést i několik tříd nebo interfaců jako pole:

autowired: [ParentClass, FooInterface]

Zkusme příklad doplnit ještě o rozhraní:

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Když službu child nijak neomezíme, bude pasovat do konstruktorů všech tříd FooDependent, BarDependent, ParentDependent i ChildDependent a autowiring ji tam předá.

Pokud její autowiring ale zúžíme na ChildClass pomocí autowired: ChildClass (nebo self), předá ji autowiring pouze do konstruktoru ChildDependent, protože vyžaduje argument typu ChildClass a platí, že ChildClass je typu ChildClass. Žádný z požadovaných typů ostatních parametrů není ChildClass ani jeho podtyp, takže se jim služba nepředá.

Pokud jej omezíme na ParentClass pomocí autowired: ParentClass, předá ji autowiring opět do konstruktoru ChildDependent (protože vyžadovaný ChildClass je podtyp ParentClass) a nově i do konstruktoru ParentDependent, protože vyžadovaný typ ParentClass je taktéž vyhovující.

Pokud jej omezíme na FooInterface, bude stále autowirovaná do ParentDependent (vyžadovaný ParentClass je podtyp FooInterface) a ChildDependent, a navíc i do konstruktoru FooDependent, nikoliv však do BarDependent, neboť BarInterface není podtyp FooInterface.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # autowiring předá do konstruktoru child
	barDep: BarDependent        # VYHODÍ VÝJIMKU, žádná služba nevyhovuje
	parentDep: ParentDependent  # autowiring předá do konstruktoru child
	childDep: ChildDependent    # autowiring předá do konstruktoru child