Nette DI Container
Nette DI je jednou z nejzajímavějších knihoven Nette. Umí generovat a automaticky aktualizovat kompilované DI kontejnery, které jsou extrémně rychlé a úžasně snadno konfigurovatelné.
Podobu služeb, které má vytvářet DI kontejner, definujeme obvykle pomocí konfiguračních souborů ve formátu NEON. Kontejner, který jsme ručně vytvořili v předchozí kapitole, by se zapsal takto:
parameters:
db:
dsn: 'mysql:'
user: root
password: '***'
services:
- Nette\Database\Connection(%db.dsn%, %db.user%, %db.password%)
- ArticleFactory
- EditController
Zápis je opravdu stručný.
Všechny závislosti deklarované v konstruktorech tříd ArticleFactory a EditController si Nette
DI samo zjistí a předá díky tzv. autowiringu,
v konfiguračním souboru proto není potřeba nic uvádět. Takže i když dojde ke změně parametrů, nemusíte
v konfiguraci nic měnit. Během vývoje Nette kontejner automaticky přegeneruje. Vy se můžete soustředit čistě na vývoj
aplikace.
Pokud chceme závislosti předávat pomocí setterů, použijeme k tomu sekci setup.
Nette DI vygeneruje přímo PHP kód kontejneru. Výsledkem je tedy soubor .php, který si můžete otevřít a
studovat. Díky tomu přesně vidíte, jak kontejner funguje. Můžete jej také debuggovat v IDE a krokovat. A hlavně:
vygenerované PHP je extrémně rychlé.
Nette DI umí také generovat kód továren na základě
dodaného rozhraní. Proto místo třídy ArticleFactory nám bude stačit vytvořit v aplikaci jen interface:
interface ArticleFactory
{
function create(): Article;
}
Celý příklad najdete na GitHubu.
Samostatné použití
Nasazení knihovny Nette DI do aplikace je velmi snadné. Nejprve ji nainstalujeme Composerem (protože stahování zipů je tááák zastaralé):
composer require nette/di
Následující kód pomocí Compileru vytvoří instanci
DI kontejneru podle konfigurace uložené v souboru config.neon:
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function ($compiler) {
$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;
Kontejner se vygeneruje jen jednou, jeho kód se zapíše do cache (adresář __DIR__ . '/temp') a při dalších
požadavcích se už jen odsud načítá.
Compiler sám o sobě zpřístupní v konfiguraci jen sekce services a parameters.
Chcete-li používat další – třeba search, decorator, di nebo
inject – nejdřív zaregistrujte jejich rozšíření. A abyste mohli registrovat rozšíření ze sekce
extensions v konfiguraci, přidejte ExtensionsExtension:
$compiler->addExtension('search', new Nette\DI\Extensions\SearchExtension($tempDir));
$compiler->addExtension('extensions', new Nette\DI\Extensions\ExtensionsExtension);
Všechna tato rozšíření za vás automaticky registruje Configurator používaný v plných Nette aplikacích.
Pokud v jednom cache adresáři udržujete několik různých kontejnerů, odlište je klíčem předaným jako druhý
argument metody load(); ten se stane součástí názvu vygenerované třídy:
$class = $loader->load(
fn($compiler) => $compiler->loadConfig(__DIR__ . '/config.neon'),
'my-key',
);
Pro vytvoření a získání služeb slouží metody getService() nebo getByType(). Takto vytvoříme
objekt EditController:
$controller = $container->getByType(EditController::class);
$controller->someMethod();
Během vývoje je užitečné aktivovat auto-refresh mód, kdy se kontejner automaticky přegeneruje, pokud dojde ke změně
jakékoliv třídy nebo konfiguračního souboru. Stačí v konstruktoru ContainerLoader uvést jako druhý argument
true.
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', true);
Práce s kontejnerem
Kromě getService() a getByType() nabízí objekt kontejneru několik dalších
užitečných metod:
getByType(string $type, bool $throw = true): ?objectvrátí službu daného typu. Pokud jako druhý argument předátefalse, vrátí místo vyhození výjimkynull, když žádná taková služba neexistuje.hasService(string $name): boolaisCreated(string $name): boolzjistí, zda je služba definovaná a zda už byla vytvořena.getParameters(): arrayvrátí všechny parametry kontejneru,getParameter($key)vrátí jeden.createInstance(string $class, array $args = []): objectvytvoří novou instanci dané třídy a předá jí závislosti konstruktoru pomocí autowiringu.callMethod(callable $function, array $args = []): mixedzavolá daný callable a předá mu argumenty pomocí autowiringu.callInjects(object $service): voidzavolá na daném objektu všechny metodyinject*()a předá jim závislosti.
Konstruktor kontejneru také přijímá pole parametrů, které doplní ty definované v konfiguraci:
$container = new $class(['host' => 'localhost']);
Použití s frameworkem Nette
Jak jsme si ukázali, použití Nette DI není limitované na aplikace psané v Nette Frameworku, můžete jej pomocí pouhých 3 řádků kódu nasadit kdekoliv. Pokud však vyvíjíte aplikace v Nette Framework, konfiguraci a vytvoření kontejneru má na starosti Bootstrap.