Dienst-Definitionen

Die Konfiguration ist der Ort, an dem wir den DI-Container anweisen, wie er die einzelnen Dienste zusammenstellen und mit anderen Abhängigkeiten verbinden soll. Nette bietet eine sehr klare und elegante Möglichkeit, dies zu erreichen.

Der Abschnitt services in der NEON-Konfigurationsdatei ist der Ort, an dem wir unsere benutzerdefinierten Dienste und deren Konfigurationen definieren. Schauen wir uns ein einfaches Beispiel für die Definition eines Dienstes namens database an, der eine Instanz der Klasse PDO darstellt:

services:
	database: PDO('sqlite::memory:')

Diese Konfiguration führt zu der folgenden Fabrikmethode im DI-Container:

public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}

Dienstnamen ermöglichen es uns, in anderen Teilen der Konfigurationsdatei auf sie zu verweisen, indem wir das Format @serviceName verwenden. Wenn es nicht notwendig ist, den Dienst zu benennen, können wir einfach einen Aufzählungspunkt verwenden:

services:
	- PDO('sqlite::memory:')

Um einen Dienst aus dem DI-Container abzurufen, können wir die Methode getService() mit dem Dienstnamen als Parameter oder die Methode getByType() mit dem Diensttyp verwenden:

$database = $container->getService('database');
$database = $container->getByType(PDO::class);

Erstellung von Diensten

In den meisten Fällen wird ein Dienst einfach durch die Instanziierung einer bestimmten Klasse erstellt. Zum Beispiel:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Wenn wir die Konfiguration um zusätzliche Schlüssel erweitern müssen, kann die Definition in mehrere Zeilen aufgeteilt werden:

services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...

Der Schlüssel create hat einen Alias factory, beide Versionen sind in der Praxis üblich. Wir empfehlen jedoch die Verwendung von create.

Konstruktorargumente oder die Erstellungsmethode können alternativ in den Schlüssel arguments geschrieben werden:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

Dienste müssen nicht nur durch einfache Instanziierung einer Klasse erzeugt werden, sondern können auch durch den Aufruf statischer Methoden oder Methoden anderer Dienste entstehen:

services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()

Beachten Sie, dass wir der Einfachheit halber anstelle von -> :: verwenden, siehe Ausdrucksmittel. Diese Fabrikmethoden werden generiert:

public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}

Der DI-Container muss den Typ des erzeugten Dienstes kennen. Wenn wir einen Dienst mit einer Methode erstellen, die keinen bestimmten Rückgabetyp hat, müssen wir diesen Typ in der Konfiguration explizit angeben:

services:
	database:
		create: DatabaseFactory::create()
		type: PDO

Argumente

Wir übergeben Argumente an Konstruktoren und Methoden auf eine Weise, die der von PHP sehr ähnlich ist:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Zur besseren Lesbarkeit können wir die Argumente in separaten Zeilen auflisten. In diesem Format ist die Verwendung von Kommas optional:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)

Sie können die Argumente auch benennen, dann brauchen Sie sich nicht um ihre Reihenfolge zu kümmern:

services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)

Wenn Sie bestimmte Argumente auslassen und ihre Standardwerte verwenden oder einen Dienst über die automatische Verdrahtung einfügen möchten, verwenden Sie einen Unterstrich:

services:
	foo: Foo(_, %appDir%)

Argumente können Dienste, Parameter und vieles mehr sein, siehe Ausdrucksmittel.

Einrichtung

Im Abschnitt setup definieren wir die Methoden, die bei der Erstellung des Dienstes aufgerufen werden sollen.

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

In PHP würde dies wie folgt aussehen:

public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}

Zusätzlich zu Methodenaufrufen können Sie auch Werte an Eigenschaften übergeben. Das Hinzufügen eines Elements zu einem Array wird ebenfalls unterstützt, aber Sie müssen es in Anführungszeichen setzen, um Kollisionen mit der NEON-Syntax zu vermeiden:

services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]

In PHP würde dies bedeuten:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}

Im Setup können Sie auch statische Methoden oder Methoden von anderen Diensten aufrufen. Wenn Sie den aktuellen Dienst als Argument übergeben müssen, verwenden Sie @self:

services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)

Beachten Sie, dass wir der Einfachheit halber anstelle von -> :: verwenden, siehe Ausdrucksmittel. Dies erzeugt die folgende Fabrikmethode:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}

Ausdrucksmittel

Nette DI bietet uns außergewöhnlich reichhaltige Ausdrucksmöglichkeiten, die es uns erlauben, fast alles zu artikulieren. In Konfigurationsdateien können wir Parameter verwenden:

# Parameter
%wwwDir%

# Wert unter einem Parameterschlüssel
%mailer.user%

# Parameter innerhalb einer Zeichenkette
'%wwwDir%/images'

Wir können auch Objekte erstellen, Methoden und Funktionen aufrufen:

# ein Objekt erstellen
DateTime()

# eine statische Methode aufrufen
Collator::create(%locale%)

# eine PHP-Funktion aufrufen
::getenv(DB_USER)

Beziehen Sie sich auf Dienste entweder durch ihren Namen oder durch ihren Typ:

# Dienst nach Name
@database

# Dienst nach Typ
@Nette\Database\Connection

Verwenden Sie die Syntax von First-Class Callables:

# creating a callback, equivalent to [@user, logout]
@user::logout(...)

Verwenden Sie Konstanten:

# Klassenkonstante
FilesystemIterator::SKIP_DOTS

# globale Konstante, die mit der PHP-Funktion constant() ermittelt wird
::constant(PHP_VERSION)

Methodenaufrufe können, genau wie in PHP, verkettet werden. Der Einfachheit halber verwenden wir anstelle von -> :: :

DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()

Diese Ausdrücke können bei der Erstellung von Diensten überall verwendet werden, in Argumenten, im Setup-Abschnitt oder in Parametern:

parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )

Besondere Funktionen

Innerhalb von Konfigurationsdateien können Sie diese speziellen Funktionen verwenden:

  • not() für die Negation von Werten
  • bool(), int(), float(), string() für verlustfreies Type Casting
  • typed() um ein Array mit allen Diensten eines bestimmten Typs zu erzeugen
  • tagged(), um ein Array aller Dienste mit einem bestimmten Tag zu erzeugen
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Im Vergleich zum konventionellen Typecasting in PHP, wie z.B. (int), wird beim verlustfreien Typecasting eine Exception für nicht-numerische Werte geworfen.

Die Funktion typed() erstellt ein Array mit allen Diensten eines bestimmten Typs (Klasse oder Schnittstelle). Sie schließt Dienste mit ausgeschaltetem Autowiring aus. Es können mehrere Typen angegeben werden, getrennt durch Kommas.

services:
	- BarsDependent( typed(Bar) )

Sie können auch automatisch ein Array von Diensten eines bestimmten Typs als Argument übergeben, indem Sie autowiring verwenden.

Die Funktion tagged() erstellt ein Array mit allen Diensten mit einem bestimmten Tag. Es können mehrere Tags aufgelistet werden, getrennt durch Kommas.

services:
	- LoggersDependent( tagged(logger) )

Fahrzeugverkabelung

Mit der Taste autowired können Sie das Autowiring-Verhalten für einen bestimmten Dienst ändern. Weitere Einzelheiten finden Sie im Kapitel über die automatische Verdrahtung.

services:
	foo:
		create: Foo
		autowired: false     # der Foo-Dienst ist vom Autowiring ausgeschlossen

Tags

Tags werden verwendet, um zusätzliche Informationen zu Diensten hinzuzufügen. Sie können einem Dienst ein oder mehrere Tags zuweisen:

services:
	foo:
		create: Foo
		tags:
			- cached

Tags können auch Werte enthalten:

services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event

Um alle Dienste mit bestimmten Tags abzurufen, können Sie die Funktion tagged() verwenden:

services:
	- LoggersDependent( tagged(logger) )

Im DI-Container können Sie mit der Methode findByTag() die Namen aller Dienste mit einem bestimmten Tag abrufen:

$names = $container->findByTag('logger');
// $names ist ein Array, das den Dienstnamen und den Tag-Wert enthält
// z.B. ['foo' => 'monolog.logger.event', ...]

Injektionsmodus

Mit dem Flag inject: true wird die Übergabe von Abhängigkeiten über öffentliche Variablen mit der inject-Annotation und den inject*() -Methoden aktiviert.

services:
	articles:
		create: App\Model\Articles
		inject: true

Standardmäßig ist inject nur für Präsentatoren aktiviert.

Änderungen am Dienst

Der DI-Container enthält viele Dienste, die entweder durch eingebaute oder durch Benutzererweiterungen hinzugefügt wurden. Sie können die Definitionen dieser Dienste direkt in der Konfiguration ändern. So können Sie beispielsweise die Klasse des Dienstes application.application, die üblicherweise Nette\Application\Application lautet, in eine andere ändern:

services:
	application.application:
		create: MyApplication
		alteration: true

Das Kennzeichen alteration ist informativ und zeigt an, dass es sich lediglich um eine Änderung eines bestehenden Dienstes handelt.

Wir können die Einrichtung auch ergänzen:

services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]

Wenn Sie einen Dienst überschreiben, möchten Sie vielleicht die ursprünglichen Argumente, Einrichtungselemente oder Tags entfernen, und hier kommt reset ins Spiel:

services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags

Wenn Sie einen Dienst, der von einer Erweiterung hinzugefügt wurde, entfernen möchten, können Sie dies wie folgt tun:

services:
	cache.journal: false
Version: 3.x