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 valeursbool()
,int()
,float()
,string()
pour le moulage de type sans pertetyped()
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