Crearea extensiilor pentru Nette DI
Generarea unui container DI, pe lângă fișierele de configurare, afectează și așa-numitele
extensiuni. Le activăm în fișierul de configurare din secțiunea extensions
.
În acest mod adăugăm extensia reprezentată de clasa BlogExtension
cu numele blog
:
extensions:
blog: BlogExtension
Fiecare extensie de compilare moștenește din Nette\DI\CompilerExtension și poate implementa următoarele metode care sunt apelate în timpul compilării DI:
- getConfigSchema()
- loadConfiguration()
- beforeCompile()
- afterCompile()
getConfigSchema()
Această metodă este apelată mai întâi. Aceasta definește schema utilizată pentru validarea parametrilor de configurare.
Extensiile sunt configurate într-o secțiune al cărei nume este același cu cel al secțiunii în care a fost adăugată
extensia, de exemplu blog
.
# același nume ca și extensia mea
blog:
postsPerPage: 10
comments: false
Vom defini o schemă care să descrie toate opțiunile de configurare, inclusiv tipurile, valorile acceptate și, eventual, valorile implicite ale acestora:
use Nette\Schema\Expect;
class BlogExtension extends Nette\DI\CompilerExtension
{
public function getConfigSchema(): Nette\Schema\Schema
{
return Expect::structure([
'postsPerPage' => Expect::int(),
'allowComments' => Expect::bool()->default(true),
]);
}
}
Consultați Schema pentru documentație. În plus, puteți specifica ce opțiuni pot
fi dinamice folosind dynamic()
, de
exemplu Expect::int()->dynamic()
.
Accesăm configurația prin intermediul $this->config
, care este un obiect stdClass
:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$num = $this->config->postPerPage;
if ($this->config->allowComments) {
// ...
}
}
}
loadConfiguration()
Această metodă este utilizată pentru a adăuga servicii la container. Acest lucru se face prin Nette\DI\ContainerBuilder:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$builder->addDefinition($this->prefix('articles'))
->setFactory(App\Model\HomepageArticles::class, ['@connection']) // sau setCreator()
->addSetup('setLogger', ['@logger']);
}
}
Convenția este de a prefixa serviciile adăugate de o extensie cu numele acesteia, astfel încât să nu apară conflicte de
nume. Acest lucru se face prin prefix()
, astfel încât, dacă extensia se numește „blog“, serviciul se va numi
blog.articles
.
Dacă trebuie să redenumim un serviciu, putem crea un alias cu numele său original pentru a menține compatibilitatea cu
trecutul. În mod similar, acest lucru este ceea ce face Nette pentru, de exemplu, routing.router
, care este, de
asemenea, disponibil sub numele anterior router
.
$builder->addAlias('router', 'routing.router');
Recuperarea serviciilor dintr-un fișier
Putem crea servicii utilizând API-ul ContainerBuilder, dar le putem adăuga și prin intermediul cunoscutului fișier de
configurare NEON și al secțiunii sale services
. Prefixul @extension
reprezintă extensia curentă.
services:
articles:
create: MyBlog\ArticlesModel(@connection)
comments:
create: MyBlog\CommentsModel(@connection, @extension.articles)
articlesList:
create: MyBlog\Components\ArticlesList(@extension.articles)
Vom adăuga servicii în acest mod:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
// încarcă fișierul de configurare pentru extensie
$this->compiler->loadDefinitionsFromConfig(
$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
);
}
}
beforeCompile()
Metoda este apelată atunci când containerul conține toate serviciile adăugate de extensiile individuale în metodele
loadConfiguration
, precum și fișierele de configurare ale utilizatorului. În această fază a asamblării, putem
apoi modifica definițiile serviciilor sau adăuga legături între ele. Puteți utiliza metoda findByTag()
pentru a
căuta servicii după etichete sau metoda findByType()
pentru a căuta după clasă sau interfață.
class BlogExtension extends Nette\DI\CompilerExtension
{
public function beforeCompile()
{
$builder = $this->getContainerBuilder();
foreach ($builder->findByTag('logaware') as $serviceName => $tagValue) {
$builder->getDefinition($serviceName)->addSetup('setLogger');
}
}
}
afterCompile()
În această fază, clasa container este deja generată ca obiect ClassType, conține toate metodele pe care serviciul le creează și este gata pentru a fi pusă în cache ca fișier PHP. În acest moment putem încă să modificăm codul clasei rezultate.
class BlogExtension extends Nette\DI\CompilerExtension
{
public function afterCompile(Nette\PhpGenerator\ClassType $class)
{
$method = $class->getMethod('__construct');
// ...
}
}
$initializare
Configuratorul apelează codul de inițializare după crearea containerului, care este creat prin scrierea într-un
obiect $this->initialization
folosind metoda
addBody().
Vom prezenta un exemplu de pornire a unei sesiuni sau de pornire a serviciilor care au eticheta run
folosind codul
de inițializare:
class BlogExtension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
// pornirea automată a sesiunii
if ($this->config->session->autoStart) {
$this->initialization->addBody('$this->getService("session")->start()');
}
// serviciile cu tag-ul "run" trebuie create după instanțierea containerului.
$builder = $this->getContainerBuilder();
foreach ($builder->findByTag('run') as $name => $foo) {
$this->initialization->addBody('$this->getService(?);', [$name]);
}
}
}