Nette Documentation Preview

syntax
Crearea extensiilor pentru Nette DI
***********************************

.[perex]
Generarea containerului DI, pe lângă fișierele de configurare, este influențată și de așa-numitele *extensii*. Le activăm în fișierul de configurare în secțiunea `extensions`.

Astfel adăugăm extensia reprezentată de clasa `BlogExtension` sub numele `blog`:

```neon
extensions:
	blog: BlogExtension
```

Fiecare extensie a compilatorului moștenește de la [api:Nette\DI\CompilerExtension] și poate implementa următoarele metode, care sunt apelate succesiv în timpul construirii containerului DI:

1. getConfigSchema()
2. loadConfiguration()
3. beforeCompile()
4. afterCompile()


getConfigSchema() .[method]
===========================

Această metodă este apelată prima. Definește schema pentru validarea parametrilor de configurare.

Configurăm extensia în secțiunea al cărei nume este același cu cel sub care a fost adăugată extensia, adică `blog`:

```neon
# același nume ca extensia
blog:
	postsPerPage: 10
	allowComments: false
```

Creăm o schemă care descrie toate opțiunile de configurare, inclusiv tipurile lor, valorile permise și, eventual, valorile implicite:

```php
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),
		]);
	}
}
```

Documentația o găsiți pe pagina [Schema |schema:]. În plus, se poate specifica ce opțiuni pot fi [dinamice |application:bootstrap#Parametri dinamici] folosind `dynamic()`, de ex. `Expect::int()->dynamic()`.

Accesăm configurația prin variabila `$this->config`, care este un obiect `stdClass`:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$num = $this->config->postPerPage;
		if ($this->config->allowComments) {
			// ...
		}
	}
}
```


loadConfiguration() .[method]
=============================

Se utilizează pentru adăugarea serviciilor în container. Pentru aceasta se folosește [api:Nette\DI\ContainerBuilder]:

```php
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 extensie cu numele său, pentru a evita conflictele de nume. Acest lucru îl face metoda `prefix()`, deci dacă extensia se numește `blog`, serviciul va purta numele `blog.articles`.

Dacă trebuie să redenumim un serviciu, putem crea un alias cu numele original pentru a menține compatibilitatea retroactivă. Nette face acest lucru similar, de exemplu, pentru serviciul `routing.router`, care este disponibil și sub numele anterior `router`.

```php
$builder->addAlias('router', 'routing.router');
```


Încărcarea serviciilor din fișier
---------------------------------

Serviciile nu trebuie create doar folosind API-ul clasei ContainerBuilder, ci și prin sintaxa cunoscută utilizată în fișierul de configurare NEON în secțiunea services. Prefixul `@extension` reprezintă extensia curentă.

```neon
services:
	articles:
		create: MyBlog\ArticlesModel(@connection)

	comments:
		create: MyBlog\CommentsModel(@connection, @extension.articles)

	articlesList:
		create: MyBlog\Components\ArticlesList(@extension.articles)
```

Încărcăm serviciile:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();

		// încărcarea fișierului de configurare pentru extensie
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}
```


beforeCompile() .[method]
=========================

Metoda este apelată în momentul în care containerul conține toate serviciile adăugate de extensiile individuale în metodele `loadConfiguration` și, de asemenea, de fișierele de configurare ale utilizatorului. În această fază a construirii, putem deci modifica definițiile serviciilor sau completa legăturile dintre ele. Pentru căutarea serviciilor în container după tag-uri se poate utiliza metoda `findByTag()`, iar după clasă sau interfață, metoda `findByType()`.

```php
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() .[method]
========================

În această fază, clasa containerului este deja generată sub forma unui obiect [ClassType |php-generator:#Clase], conține toate metodele care creează servicii și este pregătită pentru scrierea în cache. Putem încă modifica codul rezultat al clasei în acest moment.

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function afterCompile(Nette\PhpGenerator\ClassType $class)
	{
		$method = $class->getMethod('__construct');
		// ...
	}
}
```


$initialization .[method]
=========================

Clasa Configurator, după [crearea containerului |application:bootstrap#index.php], apelează codul de inițializare, care se creează prin scrierea în obiectul `$this->initialization` folosind [metoda addBody() |php-generator:#Corpul metodelor și funcțiilor].

Vom arăta un exemplu despre cum, de exemplu, să pornim sesiunea cu codul de inițializare sau să rulăm servicii care au tag-ul `run`:

```php
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]);
		}
	}
}
```

Crearea extensiilor pentru Nette DI

Generarea containerului DI, pe lângă fișierele de configurare, este influențată și de așa-numitele extensii. Le activăm în fișierul de configurare în secțiunea extensions.

Astfel adăugăm extensia reprezentată de clasa BlogExtension sub numele blog:

extensions:
	blog: BlogExtension

Fiecare extensie a compilatorului moștenește de la Nette\DI\CompilerExtension și poate implementa următoarele metode, care sunt apelate succesiv în timpul construirii containerului DI:

  1. getConfigSchema()
  2. loadConfiguration()
  3. beforeCompile()
  4. afterCompile()

getConfigSchema()

Această metodă este apelată prima. Definește schema pentru validarea parametrilor de configurare.

Configurăm extensia în secțiunea al cărei nume este același cu cel sub care a fost adăugată extensia, adică blog:

# același nume ca extensia
blog:
	postsPerPage: 10
	allowComments: false

Creăm o schemă care descrie toate opțiunile de configurare, inclusiv tipurile lor, valorile permise și, eventual, valorile implicite:

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),
		]);
	}
}

Documentația o găsiți pe pagina Schema. În plus, se poate specifica ce opțiuni pot fi dinamice folosind dynamic(), de ex. Expect::int()->dynamic().

Accesăm configurația prin variabila $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()

Se utilizează pentru adăugarea serviciilor în container. Pentru aceasta se folosește 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 extensie cu numele său, pentru a evita conflictele de nume. Acest lucru îl face metoda prefix(), deci dacă extensia se numește blog, serviciul va purta numele blog.articles.

Dacă trebuie să redenumim un serviciu, putem crea un alias cu numele original pentru a menține compatibilitatea retroactivă. Nette face acest lucru similar, de exemplu, pentru serviciul routing.router, care este disponibil și sub numele anterior router.

$builder->addAlias('router', 'routing.router');

Încărcarea serviciilor din fișier

Serviciile nu trebuie create doar folosind API-ul clasei ContainerBuilder, ci și prin sintaxa cunoscută utilizată în fișierul de configurare NEON în secțiunea 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)

Încărcăm serviciile:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();

		// încărcarea fișierului de configurare pentru extensie
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}

beforeCompile()

Metoda este apelată în momentul în care containerul conține toate serviciile adăugate de extensiile individuale în metodele loadConfiguration și, de asemenea, de fișierele de configurare ale utilizatorului. În această fază a construirii, putem deci modifica definițiile serviciilor sau completa legăturile dintre ele. Pentru căutarea serviciilor în container după tag-uri se poate utiliza metoda findByTag(), iar după clasă sau interfață, metoda findByType().

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 containerului este deja generată sub forma unui obiect ClassType, conține toate metodele care creează servicii și este pregătită pentru scrierea în cache. Putem încă modifica codul rezultat al clasei în acest moment.

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function afterCompile(Nette\PhpGenerator\ClassType $class)
	{
		$method = $class->getMethod('__construct');
		// ...
	}
}

$initialization

Clasa Configurator, după crearea containerului, apelează codul de inițializare, care se creează prin scrierea în obiectul $this->initialization folosind metoda addBody().

Vom arăta un exemplu despre cum, de exemplu, să pornim sesiunea cu codul de inițializare sau să rulăm servicii care au tag-ul run:

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]);
		}
	}
}