Nette DI Container
Nette DI est l'une des bibliothèques les plus intéressantes de Nette. Elle sait générer et mettre à jour automatiquement des conteneurs DI compilés, extrêmement rapides et remarquablement faciles à configurer.
La forme des services que le conteneur DI doit créer se définit habituellement à l'aide de fichiers de configuration au format NEON. Le conteneur que nous avons créé manuellement dans le chapitre précédent s'écrirait ainsi :
parameters:
db:
dsn: 'mysql:'
user: root
password: '***'
services:
- Nette\Database\Connection(%db.dsn%, %db.user%, %db.password%)
- ArticleFactory
- EditController
La syntaxe est très concise.
Toutes les dépendances déclarées dans les constructeurs des classes ArticleFactory et
EditController sont détectées et passées automatiquement par Nette DI grâce à ce qu'on appelle l'autowiring, il n'y a donc rien à indiquer dans le fichier de
configuration. Ainsi, même si les paramètres changent, vous n'avez rien à modifier dans la configuration. Pendant le
développement, Nette régénère le conteneur automatiquement. Vous pouvez vous concentrer purement sur le développement de
l'application.
Si nous voulons passer les dépendances à l'aide de setters, nous utilisons pour cela la section setup.
Nette DI génère directement le code PHP du conteneur. Le résultat est donc un fichier .php que vous pouvez
ouvrir et examiner. Vous voyez ainsi exactement comment le conteneur fonctionne. Vous pouvez aussi le déboguer dans votre IDE et
parcourir son exécution pas à pas. Et surtout : le code PHP généré est extrêmement rapide.
Nette DI sait également générer le code d'une factory à
partir d'une interface fournie. Au lieu de la classe ArticleFactory, il nous suffit donc de créer une interface dans
l'application :
interface ArticleFactory
{
function create(): Article;
}
Vous trouverez l'exemple complet sur GitHub.
Utilisation autonome
Intégrer la bibliothèque Nette DI dans une application est très facile. Nous l'installons d'abord à l'aide de Composer (parce que télécharger des fichiers zip, c'est tellement dépassé) :
composer require nette/di
Le code suivant utilise le Compiler pour créer une
instance du conteneur DI selon la configuration stockée dans le fichier config.neon :
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function ($compiler) {
$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;
Le conteneur n'est généré qu'une seule fois, son code est écrit dans le cache (le répertoire
__DIR__ . '/temp') et, lors des requêtes suivantes, il est seulement chargé depuis là.
Seul, le Compiler n'active dans la configuration que les sections services et
parameters. Pour utiliser les autres – comme search, decorator, di ou
inject – enregistrez d'abord leurs extensions. Et pour permettre l'enregistrement d'extensions depuis la section
extensions de la configuration, ajoutez l'ExtensionsExtension :
$compiler->addExtension('search', new Nette\DI\Extensions\SearchExtension($tempDir));
$compiler->addExtension('extensions', new Nette\DI\Extensions\ExtensionsExtension);
Le Configurator utilisé dans les applications Nette complètes les enregistre tous automatiquement.
Si vous conservez plusieurs conteneurs différents dans le même répertoire de cache, distinguez-les par une clé passée en
deuxième argument à load() ; elle fait partie du nom de la classe générée :
$class = $loader->load(
fn($compiler) => $compiler->loadConfig(__DIR__ . '/config.neon'),
'my-key',
);
Les méthodes getService() ou getByType() servent à créer et à récupérer les services. Voici
comment nous créons l'objet EditController :
$controller = $container->getByType(EditController::class);
$controller->someMethod();
Pendant le développement, il est utile d'activer le mode de rafraîchissement automatique, où le conteneur se régénère
dès qu'une classe ou un fichier de configuration est modifié. Il suffit de passer true en deuxième argument du
constructeur de ContainerLoader.
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', true);
Travailler avec le conteneur
Outre getService() et getByType(), l'objet conteneur offre plusieurs autres méthodes utiles :
getByType(string $type, bool $throw = true): ?objectrenvoie le service du type donné. Si vous passezfalseen deuxième argument, il renvoienullau lieu de lever une exception lorsqu'aucun service de ce type n'existe.hasService(string $name): booletisCreated(string $name): boolvous disent si un service est défini et s'il a déjà été instancié.getParameters(): arrayrenvoie tous les paramètres du conteneur,getParameter($key)en renvoie un seul.createInstance(string $class, array $args = []): objectcrée une nouvelle instance de la classe donnée et lui passe les dépendances du constructeur par autowiring.callMethod(callable $function, array $args = []): mixedappelle le callable donné et lui passe ses arguments par autowiring.callInjects(object $service): voidappelle toutes les méthodesinject*()de l'objet donné et leur passe les dépendances.
Le constructeur du conteneur accepte également un tableau de paramètres qui complètent ceux définis dans la configuration :
$container = new $class(['host' => 'localhost']);
Utilisation avec Nette Framework
Comme nous l'avons montré, l'utilisation de Nette DI n'est pas réservée aux applications construites avec Nette Framework ; vous pouvez l'intégrer n'importe où en seulement trois lignes de code. Cependant, si vous développez des applications à l'aide de Nette Framework, la configuration et la création du conteneur sont prises en charge par Bootstrap.