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 Wertenbool()
,int()
,float()
,string()
für verlustfreies Type Castingtyped()
um ein Array mit allen Diensten eines bestimmten Typs zu erzeugentagged()
, 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