Nette Documentation Preview

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

.[perex]
Autowiring je odlična lastnost, ki zna samodejno posredovati v konstruktor in druge metode zahtevane storitve, tako da jih sploh ni treba pisati. Prihrani vam veliko časa.

Zahvaljujoč temu lahko izpustimo večino argumentov pri pisanju definicij storitev. Namesto:

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

Zadostuje napisati:

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

Autowiring se ravna po tipih, zato mora biti za delovanje razred `ArticleRepository` definiran približno takole:

```php
namespace Model;

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

Da bi lahko uporabili autowiring, mora za vsak tip v vsebniku obstajati **točno ena storitev**. Če bi jih bilo več, autowiring ne bi vedel, katero naj posreduje, in bi vrgel izjemo:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # VRŽE IZJEMO, ustrezata mainDb in tempDb
```

Rešitev bi bila bodisi obiti autowiring in eksplicitno navesti ime storitve (tj. `articles: Model\ArticleRepository(@mainDb)`). Pametneje pa je autowiring ene od storitev [izklopiti |#Izklop autowiringa] ali prvo storitev [dati prednost |#Prednost autowiringa].


Izklop autowiringa
------------------

Autowiring storitve lahko izklopimo z uporabo možnosti `autowired: no`:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # storitev tempDb je izključena iz autowiringa

	articles: Model\ArticleRepository  # zato posreduje v konstruktor mainDb
```

Storitev `articles` ne bo vrgla izjeme, da obstajata dve ustrezni storitvi tipa `PDO` (tj. `mainDb` in `tempDb`), ki ju je mogoče posredovati v konstruktor, ker vidi samo storitev `mainDb`.

.[note]
Konfiguracija autowiringa v Nette deluje drugače kot v Symfonyju, kjer možnost `autowire: false` pove, da se autowiring ne sme uporabljati za argumente konstruktorja dane storitve. V Nette se autowiring uporablja vedno, bodisi za argumente konstruktorja ali katere koli druge metode. Možnost `autowired: false` pove, da instanca dane storitve ne sme biti nikamor posredovana z uporabo autowiringa.


Prednost autowiringa
--------------------

Če imamo več storitev istega tipa in pri eni od njih navedemo možnost `autowired`, postane ta storitev prednostna:

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # postane prednostna

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

	articles: Model\ArticleRepository
```

Storitev `articles` ne bo vrgla izjeme, da obstajata dve ustrezni storitvi tipa `PDO` (tj. `mainDb` in `tempDb`), ampak bo uporabila prednostno storitev, torej `mainDb`.


Polje storitev
--------------

Autowiring zna posredovati tudi polja storitev določenega tipa. Ker v PHP ni mogoče nativno zapisati tipa elementov polja, je treba poleg tipa `array` dopolniti tudi phpDoc komentar s tipom elementa v obliki `ClassName[]`:

```php
namespace Model;

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

DI vsebnik nato samodejno posreduje polje storitev, ki ustrezajo danemu tipu. Izpusti storitve, ki imajo izklopljen autowiring.

Tip v komentarju je lahko tudi v obliki `array<int, Class>` ali `list<Class>`. Če ne morete vplivati na obliko phpDoc komentarja, lahko polje storitev posredujete neposredno v konfiguraciji z uporabo [`typed()` |services#Posebne funkcije].


Skalarni argumenti
------------------

Autowiring zna vstavljati samo objekte in polja objektov. Skalarne argumente (npr. nize, števila, booleane) [zapišemo v konfiguraciji |services#Argumenti]. Alternativa je ustvariti [settings-objekt |best-practices:passing-settings-to-presenters], ki skalarno vrednost (ali več vrednosti) zapakira v obliko objekta, ki ga nato lahko spet posredujemo z uporabo autowiringa.

```php
class MySettings
{
	public function __construct(
		// readonly je mogoče uporabiti od PHP 8.1
		public readonly bool $value,
	)
	{}
}
```

Iz njega ustvarite storitev z dodajanjem v konfiguracijo:

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

Vsi razredi jo nato zahtevajo z uporabo autowiringa.


Omejitev autowiringa
--------------------

Posameznim storitvam lahko autowiring omejimo samo na določene razrede ali vmesnike.

Običajno autowiring storitev posreduje v vsak parameter metode, katerega tipu storitev ustreza. Omejitev pomeni, da določimo pogoje, ki jim morajo ustrezati tipi, navedeni pri parametrih metod, da jim bo storitev posredovana.

Poglejmo si to na primeru:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

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

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

Če bi jih vse registrirali kot storitve, bi autowiring spodletel:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # VRŽE IZJEMO, ustrezata storitvi parent in child
	childDep: ChildDependent    # autowiring posreduje v konstruktor storitev child
```

Storitev `parentDep` vrže izjemo `Multiple services of type ParentClass found: parent, child`, ker v njen konstruktor ustrezata obe storitvi `parent` in `child`, in autowiring ne more odločiti, katero naj izbere.

Pri storitvi `child` lahko zato omejimo njen autowiring na tip `ChildClass`:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # lahko napišemo tudi 'autowired: self'

	parentDep: ParentDependent  # autowiring posreduje v konstruktor storitev parent
	childDep: ChildDependent    # autowiring posreduje v konstruktor storitev child
```

Zdaj se v konstruktor storitve `parentDep` posreduje storitev `parent`, ker je zdaj to edini ustrezen objekt. Storitve `child` autowiring tja ne posreduje več. Da, storitev `child` je še vedno tipa `ParentClass`, vendar ne velja več omejitveni pogoj, dan za tip parametra, tj. ne velja, da je `ParentClass` *nadtip* `ChildClass`.

Pri storitvi `child` bi bilo mogoče `autowired: ChildClass` zapisati tudi kot `autowired: self`, ker je `self` nadomestno ime za razred trenutne storitve.

V ključu `autowired` je mogoče navesti tudi več razredov ali vmesnikov kot polje:

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

Poskusimo primer dopolniti še z vmesniki:

```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)
	{}
}
```

Če storitve `child` nikakor ne omejimo, bo ustrezala konstruktorjem vseh razredov `FooDependent`, `BarDependent`, `ParentDependent` in `ChildDependent`, in autowiring jo bo tja posredoval.

Če pa njen autowiring omejimo na `ChildClass` z `autowired: ChildClass` (ali `self`), jo bo autowiring posredoval samo v konstruktor `ChildDependent`, ker zahteva argument tipa `ChildClass` in velja, da je `ChildClass` *tipa* `ChildClass`. Noben drug tip, naveden pri drugih parametrih, ni nadtip `ChildClass`, zato se storitev ne posreduje.

Če jo omejimo na `ParentClass` z `autowired: ParentClass`, jo bo autowiring spet posredoval v konstruktor `ChildDependent` (ker je zahtevani `ChildClass` nadtip `ParentClass`) in na novo tudi v konstruktor `ParentDependent`, ker je zahtevani tip `ParentClass` prav tako ustrezen.

Če jo omejimo na `FooInterface`, bo še vedno avtomatsko povezana v `ParentDependent` (zahtevani `ParentClass` je nadtip `FooInterface`) in `ChildDependent`, poleg tega pa tudi v konstruktor `FooDependent`, vendar ne v `BarDependent`, ker `BarInterface` ni nadtip `FooInterface`.

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

	fooDep: FooDependent        # autowiring posreduje v konstruktor child
	barDep: BarDependent        # VRŽE IZJEMO, nobena storitev ne ustreza
	parentDep: ParentDependent  # autowiring posreduje v konstruktor child
	childDep: ChildDependent    # autowiring posreduje v konstruktor child
```

Autowiring

Autowiring je odlična lastnost, ki zna samodejno posredovati v konstruktor in druge metode zahtevane storitve, tako da jih sploh ni treba pisati. Prihrani vam veliko časa.

Zahvaljujoč temu lahko izpustimo večino argumentov pri pisanju definicij storitev. Namesto:

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

Zadostuje napisati:

services:
	articles: Model\ArticleRepository

Autowiring se ravna po tipih, zato mora biti za delovanje razred ArticleRepository definiran približno takole:

namespace Model;

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

Da bi lahko uporabili autowiring, mora za vsak tip v vsebniku obstajati točno ena storitev. Če bi jih bilo več, autowiring ne bi vedel, katero naj posreduje, in bi vrgel izjemo:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # VRŽE IZJEMO, ustrezata mainDb in tempDb

Rešitev bi bila bodisi obiti autowiring in eksplicitno navesti ime storitve (tj. articles: Model\ArticleRepository(@mainDb)). Pametneje pa je autowiring ene od storitev izklopiti ali prvo storitev dati prednost.

Izklop autowiringa

Autowiring storitve lahko izklopimo z uporabo možnosti autowired: no:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # storitev tempDb je izključena iz autowiringa

	articles: Model\ArticleRepository  # zato posreduje v konstruktor mainDb

Storitev articles ne bo vrgla izjeme, da obstajata dve ustrezni storitvi tipa PDO (tj. mainDb in tempDb), ki ju je mogoče posredovati v konstruktor, ker vidi samo storitev mainDb.

Konfiguracija autowiringa v Nette deluje drugače kot v Symfonyju, kjer možnost autowire: false pove, da se autowiring ne sme uporabljati za argumente konstruktorja dane storitve. V Nette se autowiring uporablja vedno, bodisi za argumente konstruktorja ali katere koli druge metode. Možnost autowired: false pove, da instanca dane storitve ne sme biti nikamor posredovana z uporabo autowiringa.

Prednost autowiringa

Če imamo več storitev istega tipa in pri eni od njih navedemo možnost autowired, postane ta storitev prednostna:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # postane prednostna

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

	articles: Model\ArticleRepository

Storitev articles ne bo vrgla izjeme, da obstajata dve ustrezni storitvi tipa PDO (tj. mainDb in tempDb), ampak bo uporabila prednostno storitev, torej mainDb.

Polje storitev

Autowiring zna posredovati tudi polja storitev določenega tipa. Ker v PHP ni mogoče nativno zapisati tipa elementov polja, je treba poleg tipa array dopolniti tudi phpDoc komentar s tipom elementa v obliki ClassName[]:

namespace Model;

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

DI vsebnik nato samodejno posreduje polje storitev, ki ustrezajo danemu tipu. Izpusti storitve, ki imajo izklopljen autowiring.

Tip v komentarju je lahko tudi v obliki array<int, Class> ali list<Class>. Če ne morete vplivati na obliko phpDoc komentarja, lahko polje storitev posredujete neposredno v konfiguraciji z uporabo typed().

Skalarni argumenti

Autowiring zna vstavljati samo objekte in polja objektov. Skalarne argumente (npr. nize, števila, booleane) zapišemo v konfiguraciji. Alternativa je ustvariti settings-objekt, ki skalarno vrednost (ali več vrednosti) zapakira v obliko objekta, ki ga nato lahko spet posredujemo z uporabo autowiringa.

class MySettings
{
	public function __construct(
		// readonly je mogoče uporabiti od PHP 8.1
		public readonly bool $value,
	)
	{}
}

Iz njega ustvarite storitev z dodajanjem v konfiguracijo:

services:
	- MySettings('any value')

Vsi razredi jo nato zahtevajo z uporabo autowiringa.

Omejitev autowiringa

Posameznim storitvam lahko autowiring omejimo samo na določene razrede ali vmesnike.

Običajno autowiring storitev posreduje v vsak parameter metode, katerega tipu storitev ustreza. Omejitev pomeni, da določimo pogoje, ki jim morajo ustrezati tipi, navedeni pri parametrih metod, da jim bo storitev posredovana.

Poglejmo si to na primeru:

class ParentClass
{}

class ChildClass extends ParentClass
{}

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

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

Če bi jih vse registrirali kot storitve, bi autowiring spodletel:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # VRŽE IZJEMO, ustrezata storitvi parent in child
	childDep: ChildDependent    # autowiring posreduje v konstruktor storitev child

Storitev parentDep vrže izjemo Multiple services of type ParentClass found: parent, child, ker v njen konstruktor ustrezata obe storitvi parent in child, in autowiring ne more odločiti, katero naj izbere.

Pri storitvi child lahko zato omejimo njen autowiring na tip ChildClass:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # lahko napišemo tudi 'autowired: self'

	parentDep: ParentDependent  # autowiring posreduje v konstruktor storitev parent
	childDep: ChildDependent    # autowiring posreduje v konstruktor storitev child

Zdaj se v konstruktor storitve parentDep posreduje storitev parent, ker je zdaj to edini ustrezen objekt. Storitve child autowiring tja ne posreduje več. Da, storitev child je še vedno tipa ParentClass, vendar ne velja več omejitveni pogoj, dan za tip parametra, tj. ne velja, da je ParentClass nadtip ChildClass.

Pri storitvi child bi bilo mogoče autowired: ChildClass zapisati tudi kot autowired: self, ker je self nadomestno ime za razred trenutne storitve.

V ključu autowired je mogoče navesti tudi več razredov ali vmesnikov kot polje:

autowired: [BarClass, FooInterface]

Poskusimo primer dopolniti še z vmesniki:

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)
	{}
}

Če storitve child nikakor ne omejimo, bo ustrezala konstruktorjem vseh razredov FooDependent, BarDependent, ParentDependent in ChildDependent, in autowiring jo bo tja posredoval.

Če pa njen autowiring omejimo na ChildClass z autowired: ChildClass (ali self), jo bo autowiring posredoval samo v konstruktor ChildDependent, ker zahteva argument tipa ChildClass in velja, da je ChildClass tipa ChildClass. Noben drug tip, naveden pri drugih parametrih, ni nadtip ChildClass, zato se storitev ne posreduje.

Če jo omejimo na ParentClass z autowired: ParentClass, jo bo autowiring spet posredoval v konstruktor ChildDependent (ker je zahtevani ChildClass nadtip ParentClass) in na novo tudi v konstruktor ParentDependent, ker je zahtevani tip ParentClass prav tako ustrezen.

Če jo omejimo na FooInterface, bo še vedno avtomatsko povezana v ParentDependent (zahtevani ParentClass je nadtip FooInterface) in ChildDependent, poleg tega pa tudi v konstruktor FooDependent, vendar ne v BarDependent, ker BarInterface ni nadtip FooInterface.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # autowiring posreduje v konstruktor child
	barDep: BarDependent        # VRŽE IZJEMO, nobena storitev ne ustreza
	parentDep: ParentDependent  # autowiring posreduje v konstruktor child
	childDep: ChildDependent    # autowiring posreduje v konstruktor child