Configurator
sám však žádný kód negeneruje, o to se stará Nette\DI\Compiler a Nette\DI\ContainerBuilder. Nejdříve se načtou
konfigurační soubory a předají Compiler
u. Do Compiler
u si můžeme připojit vlastní rozšíření
v config.neon
:
extensions:
blog: MyBlogExtension
Každé rozšíření Compiler
u musí dědit od Nette\DI\CompilerExtension a může implementovat tři
různé metody, které jsou postupně volány, během sestavování Container
u.
CompilerExtension::loadConfiguration()
Metoda je nad všemi rozšířeními volána jako první a je určena k načtení dodatečných konfiguračních souborů,
vytvoření dalších služeb pomocí Nette\DI\ContainerBuilder a hlavně zpracování konfigurace
aplikace.
V configu je možné uvést sekci, která se jmenuje stejně jako naše rozšíření. Pokud tedy budeme uvažovat předchozí
příklad, mohlo by v konfigu přibýt například toto
blog: # stejné jméno jako má extension
postsPerPage: 10
comments: FALSE
Nastavení si lze přečíst zavoláním metody getConfig() v rozšíření.
class MyBlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$config = $this->getConfig();
// array(2) [ 'postsPerPage' => 10, 'comments' => FALSE ]
Protože uznáváme princip konvence před
konfigurací, nastavíme si výchozí hodnoty, aby aplikace fungovala i bez toho, že budeme něco nastavovat. V prvním
argumentu metody getConfig()
můžeme poskytnout výchozí hodnoty, do kterých bude sloučena příslušná sekce
konfiguračního souboru.
class MyBlogExtension extends Nette\DI\CompilerExtension
{
public $defaults = array(
'postsPerPage' => 5,
'comments' => TRUE
);
public function loadConfiguration()
{
$config = $this->getConfig($this->defaults);
Nastavení máme v poli $config
, takže je aplikujeme na služby, které vytvoříme. Použijeme k tomu
ContainerBuilder
, který umožňuje to stejné, jako zápis služby v konfiguračním souboru.
$builder = $this->getContainerBuilder();
Vytvoříme si jako příklad službu articles
, což bude objekt třídy MyBlog\ArticlesModel
, nad
kterým ještě zavoláme metodu setLogger
.
Konvence je, prefixovat služby v rozšíření jeho názvem, aby nevznikaly konflikty. Pomůže nám s tím
metoda prefix()
.
$builder->addDefinition($this->prefix('articles'))
->setFactory('MyBlog\ArticlesModel', array('@connection'))
->addSetup('setLogger', array('@logger'));
Načítání dodatečné konfigurace
Pokud se nám nelíbí vytvářet všechny služby v rozšíření, můžeme jeho část přesunout do samostatného
konfiguračního souboru.
services:
blog.articles:
factory: MyBlog\ArticlesModel(@connection)
blog.comments:
factory: MyBlog\CommentsModel(@connection, @blog.articles)
blog.articlesList:
factory: MyBlog\Components\ArticlesList(@blog.articles)
Který načteme a dodatečně služby nastavíme
public function loadConfiguration()
{
$config = $this->getConfig($this->defaults);
$builder = $this->getContainerBuilder();
// načtení konfiguračního souboru pro rozšíření
$this->compiler->parseServices($builder, $this->loadFromFile(__DIR__ . '/blog.neon'));
// počet článků na stránku v komponentě
$builder->getDefinition($this->prefix('articlesList'))
->addSetup('setPostsPerPage', array($config['postsPerPage']));
// volitelné vypnutí komentářů
if (!$config['comments']) {
$builder->getDefinition($this->prefix('comments'))
->addSetup('disableComments');
}
}
CompilerExtension::beforeCompile()
V této fázi sestavování už by neměly přibývat další služby. Můžeme ovšem upravovat existující a doplnit
některé potřebné vazby mezi službami, například pomocí tagů.
CompilerExtension::afterCompile()
V této fázi už je třída Container
u vygenerována, obsahuje všechny metody, které vytváří služby a je
připravena na zápis do cache. Díky api třídy Nette\PhpGenerator\ClassType můžeme přidávat vlastní
kód do kontejneru a upravovat tak výsledný kód, který se stará o vytvoření služeb.
Samotný Nette Framework například přidává metodu initialize
, ve které zpracovává některá uživatelská nastavení.
Tuto metodu pak sám vždy volá po instanciování kontejneru.
Ukážeme si kousek kódu, kde Nette Framework doplňuje do metody initialize
startování session a automatické
spouštění služeb, které mají tag run
.
public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
$container = $this->getContainerBuilder();
$config = $this->getConfig($this->defaults);
// metoda initialize
$initialize = $class->methods['initialize'];
// automatické startování session
if ($config['session']['autoStart']) {
$initialize->addBody('$this->session->start();');
}
// služby s tagem run musejí být spouštěny po vytvoření kontejneru
foreach ($container->findByTag('run') as $name => $foo) {
$initialize->addBody('$this->getService(?);', array($name));
}
}