Nette DI Container
Nette DI è una delle librerie più interessanti di Nette. Sa generare e aggiornare automaticamente container DI compilati, estremamente veloci e straordinariamente facili da configurare.
La forma dei servizi che il container DI deve creare si definisce di norma con file di configurazione in formato NEON. Il container che abbiamo creato manualmente nel capitolo precedente si scriverebbe così:
parameters:
db:
dsn: 'mysql:'
user: root
password: '***'
services:
- Nette\Database\Connection(%db.dsn%, %db.user%, %db.password%)
- ArticleFactory
- EditController
La sintassi è molto concisa.
Tutte le dipendenze dichiarate nei costruttori delle classi ArticleFactory ed EditController vengono
individuate e passate automaticamente da Nette DI grazie al cosiddetto autowiring, quindi non serve
indicare nulla nel file di configurazione. Anche se i parametri cambiano, non dovete cambiare nulla nella configurazione. Durante
lo sviluppo Nette rigenera automaticamente il container. Potete concentrarvi esclusivamente sullo sviluppo dell'applicazione.
Se vogliamo passare le dipendenze con i setter, usiamo a questo scopo la sezione setup.
Nette DI genera direttamente il codice PHP del container. Il risultato è quindi un file .php che potete aprire ed
esaminare. Questo vi permette di vedere esattamente come funziona il container. Potete anche eseguirne il debug nel vostro IDE e
percorrerne l'esecuzione passo passo. E soprattutto: il codice PHP generato è estremamente veloce.
Nette DI sa generare anche il codice di una factory a partire da un'interfaccia fornita. Al posto
della classe ArticleFactory ci basta quindi creare nell'applicazione un'interfaccia:
interface ArticleFactory
{
function create(): Article;
}
Trovate l'esempio completo su GitHub.
Uso autonomo
Integrare la libreria Nette DI in un'applicazione è molto semplice. Per prima cosa la installiamo con Composer (perché scaricare file zip è ormai superato):
composer require nette/di
Il codice seguente usa il Compiler per creare un'istanza del
container DI secondo la configurazione salvata nel file config.neon:
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function ($compiler) {
$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;
Il container viene generato una sola volta, il suo codice viene scritto nella cache (la directory
__DIR__ . '/temp') e alle richieste successive viene solo caricato da lì.
Da solo, il Compiler abilita nella configurazione soltanto le sezioni services e
parameters. Per usare le altre, come search, decorator, di o
inject, registratene prima le estensioni. E per poter registrare le estensioni dalla sezione extensions
della configurazione, aggiungete la ExtensionsExtension:
$compiler->addExtension('search', new Nette\DI\Extensions\SearchExtension($tempDir));
$compiler->addExtension('extensions', new Nette\DI\Extensions\ExtensionsExtension);
Il Configurator usato nelle applicazioni Nette complete le registra tutte automaticamente.
Se tenete più container diversi nella stessa directory di cache, distingueteli con una chiave passata come secondo argomento a
load(); essa entra a far parte del nome della classe generata:
$class = $loader->load(
fn($compiler) => $compiler->loadConfig(__DIR__ . '/config.neon'),
'my-key',
);
Per creare e ottenere i servizi si usano i metodi getService() o getByType(). Ecco come creiamo
l'oggetto EditController:
$controller = $container->getByType(EditController::class);
$controller->someMethod();
Durante lo sviluppo è utile attivare la modalità di aggiornamento automatico, in cui il container si rigenera automaticamente
se una classe o un file di configurazione vengono modificati. Basta passare true come secondo argomento del
costruttore di ContainerLoader.
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', true);
Lavorare con il container
Oltre a getService() e getByType(), l'oggetto container offre altri metodi utili:
getByType(string $type, bool $throw = true): ?objectrestituisce il servizio del tipo indicato. Se passatefalsecome secondo argomento, restituiscenullinvece di sollevare un'eccezione quando un servizio del genere non esiste.hasService(string $name): booleisCreated(string $name): booldicono se un servizio è definito e se è già stato istanziato.getParameters(): arrayrestituisce tutti i parametri del container,getParameter($key)ne restituisce uno solo.createInstance(string $class, array $args = []): objectcrea una nuova istanza della classe indicata e ne passa le dipendenze del costruttore tramite autowiring.callMethod(callable $function, array $args = []): mixedchiama il callable indicato e ne passa gli argomenti tramite autowiring.callInjects(object $service): voidchiama tutti i metodiinject*()dell'oggetto indicato e vi passa le dipendenze.
Il costruttore del container accetta anche un array di parametri, che integrano quelli definiti nella configurazione:
$container = new $class(['host' => 'localhost']);
Uso con Nette Framework
Come abbiamo mostrato, l'uso di Nette DI non è limitato alle applicazioni costruite con Nette Framework: potete integrarlo ovunque con appena tre righe di codice. Se però sviluppate applicazioni con Nette Framework, della configurazione e della creazione del container si occupa Bootstrap.