Définitions des services

La configuration est l'endroit où nous indiquons au conteneur DI comment assembler des services individuels et comment les relier à d'autres dépendances. Nette fournit une méthode très claire et élégante pour y parvenir.

La section services du fichier de configuration NEON est l'endroit où nous définissons nos services personnalisés et leurs configurations. Examinons un exemple simple de définition d'un service nommé database, qui représente une instance de la classe PDO:

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

Cette configuration se traduit par la méthode d'usine suivante dans le conteneur DI:

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

Les noms des services nous permettent de les référencer dans d'autres parties du fichier de configuration, en utilisant le format @serviceName. S'il n'est pas nécessaire de nommer le service, nous pouvons simplement utiliser un point :

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

Pour récupérer un service dans le conteneur DI, nous pouvons utiliser la méthode getService() avec le nom du service comme paramètre, ou la méthode getByType() avec le type de service :

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

Création de services

Le plus souvent, nous créons un service en instanciant simplement une classe spécifique. Par exemple, nous pouvons créer un service en instanciant une classe spécifique :

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

Si nous avons besoin d'étendre la configuration avec des clés supplémentaires, la définition peut être développée en plusieurs lignes :

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

La clé create a un alias factory, les deux versions sont courantes dans la pratique. Cependant, nous recommandons d'utiliser create.

Les arguments du constructeur ou la méthode de création peuvent également être écrits dans la clé arguments:

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

Les services ne doivent pas nécessairement être créés par la simple instanciation d'une classe ; ils peuvent également résulter de l'appel de méthodes statiques ou de méthodes d'autres services :

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

Notez que pour plus de simplicité, au lieu de ->, nous utilisons ::, voir les moyens d'expression. Ces méthodes d'usine sont générées :

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

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

Le conteneur DI doit connaître le type du service créé. Si nous créons un service en utilisant une méthode qui n'a pas de type de retour spécifié, nous devons explicitement mentionner ce type dans la configuration :

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

Arguments

Nous passons des arguments aux constructeurs et aux méthodes d'une manière très similaire à celle de PHP :

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

Pour une meilleure lisibilité, nous pouvons lister les arguments sur des lignes séparées. Dans ce format, l'utilisation des virgules est optionnelle :

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

Vous pouvez également nommer les arguments, ce qui vous permet de ne pas vous soucier de leur ordre :

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

Si vous souhaitez omettre certains arguments et utiliser leurs valeurs par défaut ou insérer un service par câblage automatique, utilisez un trait de soulignement :

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

Les arguments peuvent être des services, des paramètres et bien d'autres choses encore, voir les moyens d'expression.

Configuration

Dans la section setup, nous définissons les méthodes qui doivent être appelées lors de la création du service.

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

En PHP, cela ressemblerait à

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

En plus des appels de méthodes, vous pouvez également passer des valeurs aux propriétés. L'ajout d'un élément à un tableau est également possible, mais vous devez le mettre entre guillemets pour éviter toute collision avec la syntaxe NEON :

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

En PHP, cela se traduirait par :

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

Dans la configuration, vous pouvez également appeler des méthodes statiques ou des méthodes d'autres services. Si vous devez passer le service courant comme argument, utilisez @self:

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

Notez que pour plus de simplicité, au lieu de ->, nous utilisons ::, voir les moyens d'expression. Cela génère la méthode d'usine suivante :

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

Moyens d'expression

Nette DI nous offre des capacités d'expression exceptionnellement riches, nous permettant d'articuler presque n'importe quoi. Dans les fichiers de configuration, nous pouvons utiliser des paramètres:

# paramètre
%wwwDir%

# valeur sous une clé de paramètre
%mailer.user%

# paramètre dans une chaîne de caractères
'%wwwDir%/images'

Nous pouvons également créer des objets, appeler des méthodes et des fonctions :

# créer un objet
DateTime()

# appeler une méthode statique
Collator::create(%locale%)

# appeler une fonction PHP
::getenv(DB_USER)

Faire référence aux services soit par leur nom, soit par leur type :

# service par nom
@database

# service par type
@Nette\Database\Connection

Utiliser une syntaxe d'appel de première classe :

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

Utiliser des constantes :

# constante de classe
FilesystemIterator::SKIP_DOTS

# constante globale obtenue par la fonction PHP constant()
::constant(PHP_VERSION)

Les appels de méthodes peuvent être enchaînés, comme en PHP. Pour simplifier, au lieu de ->, nous utilisons :::

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

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

Ces expressions peuvent être utilisées n'importe où lors de la création de services, dans les arguments, dans la section de configuration ou dans les paramètres:

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

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

Fonctions spéciales

Dans les fichiers de configuration, vous pouvez utiliser ces fonctions spéciales :

  • not() pour la négation des valeurs
  • bool(), int(), float(), string() pour le moulage de type sans perte
  • typed() pour générer un tableau de tous les services d'un type spécifié
  • tagged() pour créer un tableau de tous les services avec une étiquette donnée
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Par rapport au typage conventionnel en PHP, comme (int), le typage sans perte lèvera une exception pour les valeurs non numériques.

La fonction typed() crée un tableau de tous les services d'un type particulier (classe ou interface). Elle exclut les services dont le câblage automatique est désactivé. Plusieurs types peuvent être spécifiés, séparés par des virgules.

services:
	- BarsDependent( typed(Bar) )

Vous pouvez également passer automatiquement un tableau de services d'un type spécifique comme argument en utilisant le câblage automatique.

La fonction tagged() crée un tableau de tous les services ayant une balise spécifiée. Plusieurs balises peuvent être listées, séparées par des virgules.

services:
	- LoggersDependent( tagged(logger) )

Câblage automobile

La clé autowired permet de modifier le comportement de l'autocâblage pour un service particulier. Pour plus de détails, voir le chapitre sur l'autocâblage.

services:
	foo:
		create: Foo
		autowired: false     # le service foo est exclu de l'autocâblage

Tags

Les balises sont utilisées pour ajouter des informations supplémentaires aux services. Vous pouvez attribuer une ou plusieurs balises à un service :

services:
	foo:
		create: Foo
		tags:
			- cached

Les balises peuvent également comporter des valeurs :

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

Pour retrouver tous les services ayant des étiquettes spécifiques, vous pouvez utiliser la fonction tagged():

services:
	- LoggersDependent( tagged(logger) )

Dans le conteneur DI, vous pouvez obtenir les noms de tous les services avec une étiquette spécifique en utilisant la méthode findByTag():

$names = $container->findByTag('logger');
// $names est un tableau contenant le nom du service et la valeur du tag
// par exemple ['foo' => 'monolog.logger.event', ...]

Mode d'injection

L'utilisation du drapeau inject: true active le passage de dépendances via des variables publiques avec l'annotation inject et les méthodes inject*().

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

Par défaut, inject n'est activé que pour les présentateurs.

Modifications du service

Le conteneur DI contient de nombreux services ajoutés soit par des extensions intégrées, soit par des extensions utilisateur. Vous pouvez modifier les définitions de ces services directement dans la configuration. Par exemple, vous pouvez changer la classe du service application.application, qui est conventionnellement Nette\Application\Application, en quelque chose d'autre :

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

Le drapeau alteration est informatif, indiquant que nous modifions simplement un service existant.

Nous pouvons également compléter la configuration :

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

Lors de l'écrasement d'un service, il se peut que vous souhaitiez supprimer les arguments, les éléments de configuration ou les balises d'origine, et c'est là que reset s'avère utile :

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

Si vous souhaitez supprimer un service ajouté par une extension, vous pouvez procéder comme suit :

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