Nette Documentation Preview

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

.[perex]
Autowiring ist eine großartige Funktion, die automatisch die benötigten Dienste an den Konstruktor und andere Methoden übergeben kann, sodass wir sie überhaupt nicht schreiben müssen. Es spart Ihnen viel Zeit.

Dadurch können wir die meisten Argumente beim Schreiben von Dienstdefinitionen weglassen. Anstelle von:

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

Reicht es aus zu schreiben:

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

Autowiring orientiert sich an Typen, daher muss die Klasse `ArticleRepository` ungefähr so definiert sein, damit es funktioniert:

```php
namespace Model;

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

Um Autowiring verwenden zu können, muss für jeden Typ im Container **genau ein Dienst** vorhanden sein. Gäbe es mehr, wüsste Autowiring nicht, welchen er übergeben soll, und würde eine Ausnahme auslösen:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # WIRFT EINE AUSNAHME, sowohl mainDb als auch tempDb passen
```

Die Lösung wäre entweder, Autowiring zu umgehen und den Dienstnamen explizit anzugeben (d.h. `articles: Model\ArticleRepository(@mainDb)`). Geschickter ist es jedoch, das Autowiring für einen der Dienste [zu deaktivieren |#Deaktivieren des Autowirings] oder den ersten Dienst [zu bevorzugen |#Bevorzugung beim Autowiring].


Deaktivieren des Autowirings
----------------------------

Wir können das Autowiring eines Dienstes mit der Option `autowired: no` deaktivieren:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # Der Dienst tempDb wird vom Autowiring ausgeschlossen

	articles: Model\ArticleRepository  # übergibt daher mainDb an den Konstruktor
```

Der Dienst `articles` löst keine Ausnahme aus, dass zwei passende Dienste vom Typ `PDO` (d.h. `mainDb` und `tempDb`) existieren, die an den Konstruktor übergeben werden können, da er nur den Dienst `mainDb` sieht.

.[note]
Die Konfiguration des Autowirings in Nette funktioniert anders als in Symfony, wo die Option `autowire: false` besagt, dass Autowiring nicht für die Konstruktorargumente des betreffenden Dienstes verwendet werden soll. In Nette wird Autowiring immer verwendet, sei es für Konstruktorargumente oder für andere Methoden. Die Option `autowired: false` besagt, dass die Instanz des betreffenden Dienstes nirgendwo per Autowiring übergeben werden soll.


Bevorzugung beim Autowiring
---------------------------

Wenn wir mehrere Dienste desselben Typs haben und bei einem davon die Option `autowired` angeben, wird dieser Dienst bevorzugt:

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

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

	articles: Model\ArticleRepository
```

Der Dienst `articles` löst keine Ausnahme aus, dass zwei passende Dienste vom Typ `PDO` (d.h. `mainDb` und `tempDb`) existieren, sondern verwendet den bevorzugten Dienst, also `mainDb`.


Array von Diensten
------------------

Autowiring kann auch Arrays von Diensten eines bestimmten Typs übergeben. Da in PHP der Typ der Array-Elemente nicht nativ angegeben werden kann, muss zusätzlich zum Typ `array` ein phpDoc-Kommentar mit dem Elementtyp im Format `ClassName[]` hinzugefügt werden:

```php
namespace Model;

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

Der DI-Container übergibt dann automatisch ein Array von Diensten, die dem angegebenen Typ entsprechen. Dienste, deren Autowiring deaktiviert ist, werden ausgelassen.

Der Typ im Kommentar kann auch im Format `array<int, Class>` oder `list<Class>` vorliegen. Wenn Sie die Form des phpDoc-Kommentars nicht beeinflussen können, können Sie das Array von Diensten direkt in der Konfiguration mithilfe von [`typed()` |services#Spezielle Funktionen] übergeben.


Skalare Argumente
-----------------

Autowiring kann nur Objekte und Arrays von Objekten einfügen. Skalare Argumente (z. B. Zeichenketten, Zahlen, Booleans) [schreiben wir in der Konfiguration |services#Argumente]. Eine Alternative ist die Erstellung eines [Einstellungsobjekts |best-practices:passing-settings-to-presenters], das den skalaren Wert (oder mehrere Werte) in ein Objekt kapselt, welches dann wieder per Autowiring übergeben werden kann.

```php
class MySettings
{
	public function __construct(
		// readonly kann ab PHP 8.1 verwendet werden
		public readonly bool $value,
	)
	{}
}
```

Sie erstellen daraus einen Dienst, indem Sie ihn zur Konfiguration hinzufügen:

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

Alle Klassen fordern ihn dann per Autowiring an.


Einschränken des Autowirings
----------------------------

Für einzelne Dienste kann das Autowiring auf bestimmte Klassen oder Schnittstellen eingeschränkt werden.

Normalerweise übergibt Autowiring einen Dienst an jeden Methodenparameter, dessen Typ dem Dienst entspricht. Die Einschränkung bedeutet, dass wir Bedingungen festlegen, denen die bei den Methodenparametern angegebenen Typen entsprechen müssen, damit der Dienst an sie übergeben wird.

Zeigen wir dies an einem Beispiel:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

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

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

Wenn wir sie alle als Dienste registrieren würden, würde Autowiring fehlschlagen:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # WIRFT EINE AUSNAHME, sowohl parent als auch child passen
	childDep: ChildDependent    # Autowiring übergibt den Dienst child an den Konstruktor
```

Der Dienst `parentDep` löst die Ausnahme `Multiple services of type ParentClass found: parent, child` aus, da beide Dienste `parent` und `child` in seinen Konstruktor passen und Autowiring nicht entscheiden kann, welchen es wählen soll.

Für den Dienst `child` können wir daher sein Autowiring auf den Typ `ChildClass` einschränken:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # kann auch 'autowired: self' geschrieben werden

	parentDep: ParentDependent  # Autowiring übergibt den Dienst parent an den Konstruktor
	childDep: ChildDependent    # Autowiring übergibt den Dienst child an den Konstruktor
```

Nun wird der Dienst `parent` an den Konstruktor von `parentDep` übergeben, da er jetzt das einzige passende Objekt ist. Der Dienst `child` wird dort vom Autowiring nicht mehr übergeben. Ja, der Dienst `child` ist immer noch vom Typ `ParentClass`, aber die einschränkende Bedingung für den Parametertyp gilt nicht mehr, d.h. es gilt nicht, dass `ParentClass` *ein Supertyp* von `ChildClass` ist.

Für den Dienst `child` könnte `autowired: ChildClass` auch als `autowired: self` geschrieben werden, da `self` ein Platzhalter für die Klasse des aktuellen Dienstes ist.

Im Schlüssel `autowired` können auch mehrere Klassen oder Schnittstellen als Array angegeben werden:

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

Ergänzen wir das Beispiel noch um Schnittstellen:

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

Wenn wir den Dienst `child` nicht einschränken, passt er in die Konstruktoren aller Klassen `FooDependent`, `BarDependent`, `ParentDependent` und `ChildDependent`, und Autowiring übergibt ihn dorthin.

Wenn wir sein Autowiring jedoch auf `ChildClass` mit `autowired: ChildClass` (oder `self`) einschränken, übergibt Autowiring ihn nur an den Konstruktor von `ChildDependent`, da dieser ein Argument vom Typ `ChildClass` erfordert und `ChildClass` *vom Typ* `ChildClass` ist. Kein anderer bei den weiteren Parametern angegebener Typ ist ein Supertyp von `ChildClass`, daher wird der Dienst nicht übergeben.

Wenn wir ihn auf `ParentClass` mit `autowired: ParentClass` beschränken, übergibt Autowiring ihn erneut an den Konstruktor von `ChildDependent` (da das erforderliche `ChildClass` ein Supertyp von `ParentClass` ist) und neu auch an den Konstruktor von `ParentDependent`, da der erforderliche Typ `ParentClass` ebenfalls passend ist.

Wenn wir ihn auf `FooInterface` beschränken, wird er immer noch in `ParentDependent` (erforderliches `ParentClass` ist Supertyp von `FooInterface`) und `ChildDependent` autowired, aber zusätzlich auch in den Konstruktor von `FooDependent`, jedoch nicht in `BarDependent`, da `BarInterface` kein Supertyp von `FooInterface` ist.

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

	fooDep: FooDependent        # Autowiring übergibt child an den Konstruktor
	barDep: BarDependent        # WIRFT EINE AUSNAHME, kein Dienst passt
	parentDep: ParentDependent  # Autowiring übergibt child an den Konstruktor
	childDep: ChildDependent    # Autowiring übergibt child an den Konstruktor
```

Autowiring

Autowiring ist eine großartige Funktion, die automatisch die benötigten Dienste an den Konstruktor und andere Methoden übergeben kann, sodass wir sie überhaupt nicht schreiben müssen. Es spart Ihnen viel Zeit.

Dadurch können wir die meisten Argumente beim Schreiben von Dienstdefinitionen weglassen. Anstelle von:

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

Reicht es aus zu schreiben:

services:
	articles: Model\ArticleRepository

Autowiring orientiert sich an Typen, daher muss die Klasse ArticleRepository ungefähr so definiert sein, damit es funktioniert:

namespace Model;

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

Um Autowiring verwenden zu können, muss für jeden Typ im Container genau ein Dienst vorhanden sein. Gäbe es mehr, wüsste Autowiring nicht, welchen er übergeben soll, und würde eine Ausnahme auslösen:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # WIRFT EINE AUSNAHME, sowohl mainDb als auch tempDb passen

Die Lösung wäre entweder, Autowiring zu umgehen und den Dienstnamen explizit anzugeben (d.h. articles: Model\ArticleRepository(@mainDb)). Geschickter ist es jedoch, das Autowiring für einen der Dienste zu deaktivieren oder den ersten Dienst zu bevorzugen.

Deaktivieren des Autowirings

Wir können das Autowiring eines Dienstes mit der Option autowired: no deaktivieren:

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

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # Der Dienst tempDb wird vom Autowiring ausgeschlossen

	articles: Model\ArticleRepository  # übergibt daher mainDb an den Konstruktor

Der Dienst articles löst keine Ausnahme aus, dass zwei passende Dienste vom Typ PDO (d.h. mainDb und tempDb) existieren, die an den Konstruktor übergeben werden können, da er nur den Dienst mainDb sieht.

Die Konfiguration des Autowirings in Nette funktioniert anders als in Symfony, wo die Option autowire: false besagt, dass Autowiring nicht für die Konstruktorargumente des betreffenden Dienstes verwendet werden soll. In Nette wird Autowiring immer verwendet, sei es für Konstruktorargumente oder für andere Methoden. Die Option autowired: false besagt, dass die Instanz des betreffenden Dienstes nirgendwo per Autowiring übergeben werden soll.

Bevorzugung beim Autowiring

Wenn wir mehrere Dienste desselben Typs haben und bei einem davon die Option autowired angeben, wird dieser Dienst bevorzugt:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # wird bevorzugt

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

	articles: Model\ArticleRepository

Der Dienst articles löst keine Ausnahme aus, dass zwei passende Dienste vom Typ PDO (d.h. mainDb und tempDb) existieren, sondern verwendet den bevorzugten Dienst, also mainDb.

Array von Diensten

Autowiring kann auch Arrays von Diensten eines bestimmten Typs übergeben. Da in PHP der Typ der Array-Elemente nicht nativ angegeben werden kann, muss zusätzlich zum Typ array ein phpDoc-Kommentar mit dem Elementtyp im Format ClassName[] hinzugefügt werden:

namespace Model;

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

Der DI-Container übergibt dann automatisch ein Array von Diensten, die dem angegebenen Typ entsprechen. Dienste, deren Autowiring deaktiviert ist, werden ausgelassen.

Der Typ im Kommentar kann auch im Format array<int, Class> oder list<Class> vorliegen. Wenn Sie die Form des phpDoc-Kommentars nicht beeinflussen können, können Sie das Array von Diensten direkt in der Konfiguration mithilfe von typed() übergeben.

Skalare Argumente

Autowiring kann nur Objekte und Arrays von Objekten einfügen. Skalare Argumente (z. B. Zeichenketten, Zahlen, Booleans) schreiben wir in der Konfiguration. Eine Alternative ist die Erstellung eines Einstellungsobjekts, das den skalaren Wert (oder mehrere Werte) in ein Objekt kapselt, welches dann wieder per Autowiring übergeben werden kann.

class MySettings
{
	public function __construct(
		// readonly kann ab PHP 8.1 verwendet werden
		public readonly bool $value,
	)
	{}
}

Sie erstellen daraus einen Dienst, indem Sie ihn zur Konfiguration hinzufügen:

services:
	- MySettings('any value')

Alle Klassen fordern ihn dann per Autowiring an.

Einschränken des Autowirings

Für einzelne Dienste kann das Autowiring auf bestimmte Klassen oder Schnittstellen eingeschränkt werden.

Normalerweise übergibt Autowiring einen Dienst an jeden Methodenparameter, dessen Typ dem Dienst entspricht. Die Einschränkung bedeutet, dass wir Bedingungen festlegen, denen die bei den Methodenparametern angegebenen Typen entsprechen müssen, damit der Dienst an sie übergeben wird.

Zeigen wir dies an einem Beispiel:

class ParentClass
{}

class ChildClass extends ParentClass
{}

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

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

Wenn wir sie alle als Dienste registrieren würden, würde Autowiring fehlschlagen:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # WIRFT EINE AUSNAHME, sowohl parent als auch child passen
	childDep: ChildDependent    # Autowiring übergibt den Dienst child an den Konstruktor

Der Dienst parentDep löst die Ausnahme Multiple services of type ParentClass found: parent, child aus, da beide Dienste parent und child in seinen Konstruktor passen und Autowiring nicht entscheiden kann, welchen es wählen soll.

Für den Dienst child können wir daher sein Autowiring auf den Typ ChildClass einschränken:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # kann auch 'autowired: self' geschrieben werden

	parentDep: ParentDependent  # Autowiring übergibt den Dienst parent an den Konstruktor
	childDep: ChildDependent    # Autowiring übergibt den Dienst child an den Konstruktor

Nun wird der Dienst parent an den Konstruktor von parentDep übergeben, da er jetzt das einzige passende Objekt ist. Der Dienst child wird dort vom Autowiring nicht mehr übergeben. Ja, der Dienst child ist immer noch vom Typ ParentClass, aber die einschränkende Bedingung für den Parametertyp gilt nicht mehr, d.h. es gilt nicht, dass ParentClass ein Supertyp von ChildClass ist.

Für den Dienst child könnte autowired: ChildClass auch als autowired: self geschrieben werden, da self ein Platzhalter für die Klasse des aktuellen Dienstes ist.

Im Schlüssel autowired können auch mehrere Klassen oder Schnittstellen als Array angegeben werden:

autowired: [BarClass, FooInterface]

Ergänzen wir das Beispiel noch um Schnittstellen:

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

Wenn wir den Dienst child nicht einschränken, passt er in die Konstruktoren aller Klassen FooDependent, BarDependent, ParentDependent und ChildDependent, und Autowiring übergibt ihn dorthin.

Wenn wir sein Autowiring jedoch auf ChildClass mit autowired: ChildClass (oder self) einschränken, übergibt Autowiring ihn nur an den Konstruktor von ChildDependent, da dieser ein Argument vom Typ ChildClass erfordert und ChildClass vom Typ ChildClass ist. Kein anderer bei den weiteren Parametern angegebener Typ ist ein Supertyp von ChildClass, daher wird der Dienst nicht übergeben.

Wenn wir ihn auf ParentClass mit autowired: ParentClass beschränken, übergibt Autowiring ihn erneut an den Konstruktor von ChildDependent (da das erforderliche ChildClass ein Supertyp von ParentClass ist) und neu auch an den Konstruktor von ParentDependent, da der erforderliche Typ ParentClass ebenfalls passend ist.

Wenn wir ihn auf FooInterface beschränken, wird er immer noch in ParentDependent (erforderliches ParentClass ist Supertyp von FooInterface) und ChildDependent autowired, aber zusätzlich auch in den Konstruktor von FooDependent, jedoch nicht in BarDependent, da BarInterface kein Supertyp von FooInterface ist.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # Autowiring übergibt child an den Konstruktor
	barDep: BarDependent        # WIRFT EINE AUSNAHME, kein Dienst passt
	parentDep: ParentDependent  # Autowiring übergibt child an den Konstruktor
	childDep: ChildDependent    # Autowiring übergibt child an den Konstruktor