Nette Documentation Preview

syntax
Generierte Factories
********************

.[perex]
Nette DI kann den Code von Factories automatisch anhand von Interfaces erzeugen und erspart Ihnen damit das Schreiben von Code.

Eine Factory ist eine Klasse, die dafür zuständig ist, Objekte zu erzeugen und ihnen ihre Abhängigkeiten zu übergeben. Bitte verwechseln Sie das nicht mit dem Entwurfsmuster *Factory Method*, das eine bestimmte Art beschreibt, Factories zu nutzen, und mit diesem Thema nichts zu tun hat.

Wie eine solche Factory aussieht, haben wir im [einführenden Kapitel |introduction#Factory] gezeigt:

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

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

Nette DI kann den Code einer Factory automatisch erzeugen. Sie müssen nur ein Interface anlegen, und Nette DI erzeugt die Implementierung. Das Interface muss genau eine Methode namens `create` haben und einen Rückgabetyp deklarieren:

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

Die Factory `ArticleFactory` hat also eine Methode `create`, die `Article`-Objekte erzeugt. Die Klasse `Article` könnte zum Beispiel so aussehen:

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

Fügen Sie die Factory der Konfigurationsdatei hinzu:

```neon
services:
	- ArticleFactory
```

Nette DI erzeugt die passende Implementierung der Factory.

In dem Code, der die Factory verwendet, fordern Sie das Objekt über sein Interface an, und Nette DI liefert die erzeugte Implementierung:

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

	public function foo()
	{
		// die Factory das Objekt erzeugen lassen
		$article = $this->articleFactory->create();
	}
}
```


Factory mit Parametern
======================

Die Methode `create` der Factory kann Parameter entgegennehmen, die sie dann an den Konstruktor weiterreicht. Ergänzen wir die Klasse `Article` zum Beispiel um die ID des Autors des Artikels:

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

Den Parameter ergänzen wir auch in der Factory:

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

Weil der Name des Parameters im Konstruktor (`$authorId`) mit dem Namen des Parameters in der Methode der Factory übereinstimmt, übergibt Nette DI ihn automatisch.


Erweiterte Definition
=====================

Die Definition lässt sich über den Schlüssel `implement` auch mehrzeilig schreiben:

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

Diese längere Schreibweise erlaubt es, über den Schlüssel `arguments` weitere Argumente für den Konstruktor anzugeben und über `setup` weiter zu konfigurieren, ganz wie bei gewöhnlichen Service-Definitionen.

Ein Beispiel: Nähme die Methode `create()` den Parameter `$authorId` nicht entgegen, könnten wir in der Konfiguration einen festen Wert angeben, der dem Konstruktor von `Article` übergeben wird:

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

Nähme `create()` umgekehrt `$authorId` entgegen, wäre der Wert aber nicht Teil des Konstruktors, sondern würde über eine Methode wie `Article::setAuthorId()` übergeben, verweisen wir im Abschnitt `setup` auf den Parameter:

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


Accessor
========

Neben Factories kann Nette auch sogenannte Accessors erzeugen. Das sind Objekte mit einer Methode `get()`, die einen bestimmten Service aus dem DI-Container zurückgibt. Wiederholte Aufrufe von `get()` liefern immer dieselbe Instanz.

Accessors bieten Lazy Loading für Abhängigkeiten. Stellen Sie sich eine Klasse vor, die Fehler in eine eigene Datenbank protokolliert. Bekäme diese Klasse die Datenbankverbindung über Dependency Injection im Konstruktor, würde die Verbindung immer aufgebaut, auch wenn Fehler selten auftreten und die Verbindung die meiste Zeit ungenutzt bleibt. Stattdessen kann die Klasse einen Accessor bekommen. Das Objekt der Datenbank (die Verbindung) entsteht erst dann, wenn die Methode `get()` des Accessors zum ersten Mal aufgerufen wird.

Wie erzeugt man einen Accessor? Schreiben Sie einfach ein Interface, und Nette DI erzeugt die Implementierung. Das Interface muss genau eine Methode namens `get` haben, die keine Parameter entgegennimmt und den Rückgabetyp deklariert:

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

Fügen Sie den Accessor der Konfigurationsdatei hinzu, zusammen mit der Definition des Services, den er zurückgeben soll:

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

Weil der Accessor einen `PDO`-Service zurückgibt und in der Konfiguration nur ein einziger solcher Service definiert ist, gibt der Accessor genau diesen Service zurück. Gibt es mehrere Services dieses Typs, geben Sie über den Namen an, welchen der Accessor zurückgeben soll, etwa `- PDOAccessor(@db1)`.


Multifactory/Accessor
=====================

Bisher konnten unsere Factories und Accessors nur einen einzigen Typ von Objekt erzeugen bzw. zurückgeben. Sie können aber leicht Multifactories bauen, die die Fähigkeiten von Factories und Accessors vereinen. Das Interface einer solchen Komponente kann mehrere Methoden namens `create<Name>()` und `get<Name>()` enthalten, zum Beispiel:

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

Statt mehrere einzelne Factories und Accessors zu injizieren, können Sie also eine einzige, umfassendere Komponente injizieren.

Alternativ lässt sich statt mehrerer Methoden ein `get()` mit einem Parameter verwenden:

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

Dann tut `MultiFactory::getDb()` dasselbe wie `MultiFactoryAlt::get('db')`. Diese alternative Schreibweise hat allerdings den Nachteil, dass aus der Signatur des Interfaces nicht ausdrücklich hervorgeht, welche Werte für `$name` unterstützt werden. Außerdem lassen sich im Interface für verschiedene Werte von `$name` keine unterschiedlichen Rückgabetypen festlegen.

Statt `get($name)` kann das Interface `create($name)` deklarieren, das bei jedem Aufruf eine neue Instanz zurückgibt (während `get()` eine gemeinsame liefert). Das Interface darf nur eine einzige solche Methode mit Parameter enthalten. Ist der Rückgabetyp der Methode nullable (etwa `?PDO`), gibt sie bei einem unbekannten `$name` statt einer Exception den Wert `null` zurück.


Definition über eine Liste
--------------------------
Eine Multifactory lässt sich in der Konfiguration über eine Liste definieren, wobei die Services inline geschrieben werden: .{data-version:3.2.0}

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

Alternativ können Sie in der Definition der Multifactory über Referenzen auf bestehende Services verweisen:

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


Definition über Tags
--------------------

Eine weitere Möglichkeit, eine Multifactory zu definieren, sind [Tags |services#Tags]. Der Wert des Tags bestimmt den Namen der zugehörigen Methode:

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

	- MultiFactory(tagged: multi)
```

Generierte Factories

Nette DI kann den Code von Factories automatisch anhand von Interfaces erzeugen und erspart Ihnen damit das Schreiben von Code.

Eine Factory ist eine Klasse, die dafür zuständig ist, Objekte zu erzeugen und ihnen ihre Abhängigkeiten zu übergeben. Bitte verwechseln Sie das nicht mit dem Entwurfsmuster Factory Method, das eine bestimmte Art beschreibt, Factories zu nutzen, und mit diesem Thema nichts zu tun hat.

Wie eine solche Factory aussieht, haben wir im einführenden Kapitel gezeigt:

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

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

Nette DI kann den Code einer Factory automatisch erzeugen. Sie müssen nur ein Interface anlegen, und Nette DI erzeugt die Implementierung. Das Interface muss genau eine Methode namens create haben und einen Rückgabetyp deklarieren:

interface ArticleFactory
{
	function create(): Article;
}

Die Factory ArticleFactory hat also eine Methode create, die Article-Objekte erzeugt. Die Klasse Article könnte zum Beispiel so aussehen:

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

Fügen Sie die Factory der Konfigurationsdatei hinzu:

services:
	- ArticleFactory

Nette DI erzeugt die passende Implementierung der Factory.

In dem Code, der die Factory verwendet, fordern Sie das Objekt über sein Interface an, und Nette DI liefert die erzeugte Implementierung:

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

	public function foo()
	{
		// die Factory das Objekt erzeugen lassen
		$article = $this->articleFactory->create();
	}
}

Factory mit Parametern

Die Methode create der Factory kann Parameter entgegennehmen, die sie dann an den Konstruktor weiterreicht. Ergänzen wir die Klasse Article zum Beispiel um die ID des Autors des Artikels:

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

Den Parameter ergänzen wir auch in der Factory:

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

Weil der Name des Parameters im Konstruktor ($authorId) mit dem Namen des Parameters in der Methode der Factory übereinstimmt, übergibt Nette DI ihn automatisch.

Erweiterte Definition

Die Definition lässt sich über den Schlüssel implement auch mehrzeilig schreiben:

services:
	articleFactory:
		implement: ArticleFactory

Diese längere Schreibweise erlaubt es, über den Schlüssel arguments weitere Argumente für den Konstruktor anzugeben und über setup weiter zu konfigurieren, ganz wie bei gewöhnlichen Service-Definitionen.

Ein Beispiel: Nähme die Methode create() den Parameter $authorId nicht entgegen, könnten wir in der Konfiguration einen festen Wert angeben, der dem Konstruktor von Article übergeben wird:

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

Nähme create() umgekehrt $authorId entgegen, wäre der Wert aber nicht Teil des Konstruktors, sondern würde über eine Methode wie Article::setAuthorId() übergeben, verweisen wir im Abschnitt setup auf den Parameter:

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

Accessor

Neben Factories kann Nette auch sogenannte Accessors erzeugen. Das sind Objekte mit einer Methode get(), die einen bestimmten Service aus dem DI-Container zurückgibt. Wiederholte Aufrufe von get() liefern immer dieselbe Instanz.

Accessors bieten Lazy Loading für Abhängigkeiten. Stellen Sie sich eine Klasse vor, die Fehler in eine eigene Datenbank protokolliert. Bekäme diese Klasse die Datenbankverbindung über Dependency Injection im Konstruktor, würde die Verbindung immer aufgebaut, auch wenn Fehler selten auftreten und die Verbindung die meiste Zeit ungenutzt bleibt. Stattdessen kann die Klasse einen Accessor bekommen. Das Objekt der Datenbank (die Verbindung) entsteht erst dann, wenn die Methode get() des Accessors zum ersten Mal aufgerufen wird.

Wie erzeugt man einen Accessor? Schreiben Sie einfach ein Interface, und Nette DI erzeugt die Implementierung. Das Interface muss genau eine Methode namens get haben, die keine Parameter entgegennimmt und den Rückgabetyp deklariert:

interface PDOAccessor
{
	function get(): PDO;
}

Fügen Sie den Accessor der Konfigurationsdatei hinzu, zusammen mit der Definition des Services, den er zurückgeben soll:

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

Weil der Accessor einen PDO-Service zurückgibt und in der Konfiguration nur ein einziger solcher Service definiert ist, gibt der Accessor genau diesen Service zurück. Gibt es mehrere Services dieses Typs, geben Sie über den Namen an, welchen der Accessor zurückgeben soll, etwa - PDOAccessor(@db1).

Multifactory/Accessor

Bisher konnten unsere Factories und Accessors nur einen einzigen Typ von Objekt erzeugen bzw. zurückgeben. Sie können aber leicht Multifactories bauen, die die Fähigkeiten von Factories und Accessors vereinen. Das Interface einer solchen Komponente kann mehrere Methoden namens create<Name>() und get<Name>() enthalten, zum Beispiel:

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

Statt mehrere einzelne Factories und Accessors zu injizieren, können Sie also eine einzige, umfassendere Komponente injizieren.

Alternativ lässt sich statt mehrerer Methoden ein get() mit einem Parameter verwenden:

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

Dann tut MultiFactory::getDb() dasselbe wie MultiFactoryAlt::get('db'). Diese alternative Schreibweise hat allerdings den Nachteil, dass aus der Signatur des Interfaces nicht ausdrücklich hervorgeht, welche Werte für $name unterstützt werden. Außerdem lassen sich im Interface für verschiedene Werte von $name keine unterschiedlichen Rückgabetypen festlegen.

Statt get($name) kann das Interface create($name) deklarieren, das bei jedem Aufruf eine neue Instanz zurückgibt (während get() eine gemeinsame liefert). Das Interface darf nur eine einzige solche Methode mit Parameter enthalten. Ist der Rückgabetyp der Methode nullable (etwa ?PDO), gibt sie bei einem unbekannten $name statt einer Exception den Wert null zurück.

Definition über eine Liste

Eine Multifactory lässt sich in der Konfiguration über eine Liste definieren, wobei die Services inline geschrieben werden:

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

Alternativ können Sie in der Definition der Multifactory über Referenzen auf bestehende Services verweisen:

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

Definition über Tags

Eine weitere Möglichkeit, eine Multifactory zu definieren, sind Tags. Der Wert des Tags bestimmt den Namen der zugehörigen Methode:

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

	- MultiFactory(tagged: multi)