Nette Documentation Preview

syntax
Creación de extensiones para Nette DI
*************************************

.[perex]
La generación del contenedor DI, además de los archivos de configuración, también está influenciada por las llamadas *extensiones*. Las activamos en el archivo de configuración en la sección `extensions`.

Así es como agregamos una extensión representada por la clase `BlogExtension` bajo el nombre `blog`:

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

Cada extensión del compilador hereda de [api:Nette\DI\CompilerExtension] y puede implementar los siguientes métodos, que se llaman secuencialmente durante la construcción del contenedor DI:

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


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

Este método se llama primero. Define el esquema para validar los parámetros de configuración.

Configuramos la extensión en la sección cuyo nombre es el mismo que aquel bajo el cual se agregó la extensión, es decir, `blog`:

```neon
# mismo nombre que la extensión
blog:
	postsPerPage: 10
	allowComments: false
```

Creamos un esquema que describe todas las opciones de configuración, incluidos sus tipos, valores permitidos y, opcionalmente, valores predeterminados:

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

Encontrará la documentación en la página [Schema |schema:]. Además, se puede especificar qué opciones pueden ser [dinámicas |application:bootstrap#Dynamické parametry] usando `dynamic()`, por ejemplo, `Expect::int()->dynamic()`.

Accedemos a la configuración a través de la variable `$this->config`, que es un objeto `stdClass`:

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


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

Se utiliza para agregar servicios al contenedor. Para esto se utiliza [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']) // o setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}
```

La convención es prefijar los servicios agregados por la extensión con su nombre para evitar conflictos de nombres. Esto lo hace el método `prefix()`, por lo que si la extensión se llama `blog`, el servicio se llamará `blog.articles`.

Si necesitamos renombrar un servicio, podemos crear un alias con el nombre original para mantener la compatibilidad hacia atrás. Nette hace algo similar, por ejemplo, con el servicio `routing.router`, que también está disponible bajo el nombre anterior `router`.

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


Carga de servicios desde un archivo
-----------------------------------

No tenemos que crear servicios solo usando la API de la clase ContainerBuilder, sino también con la notación familiar utilizada en el archivo de configuración NEON en la sección de servicios. El prefijo `@extension` representa la extensión actual.

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

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

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

Cargamos los servicios:

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

		// cargar el archivo de configuración para la extensión
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}
```


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

El método se llama cuando el contenedor contiene todos los servicios agregados por las extensiones individuales en los métodos `loadConfiguration` y también por los archivos de configuración del usuario. En esta etapa de construcción, podemos modificar las definiciones de servicios o agregar enlaces entre ellos. Para buscar servicios en el contenedor por tags, se puede usar el método `findByTag()`, y por clase o interfaz, el método `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]
========================

En esta etapa, la clase del contenedor ya está generada en forma de objeto [ClassType |php-generator:#tridy], contiene todos los métodos que crean servicios y está lista para ser escrita en la caché. Todavía podemos modificar el código de la clase resultante en este momento.

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


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

La clase Configurator, después de [crear el contenedor |application:bootstrap#index.php], llama al código de inicialización, que se crea escribiendo en el objeto `$this->initialization` usando el [método addBody() |php-generator:#tela-metod-a-funkci].

Mostraremos un ejemplo de cómo iniciar la sesión con código de inicialización o ejecutar servicios que tienen el tag `run`:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// inicio automático de la sesión
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// los servicios con el tag run deben crearse después de instanciar el contenedor
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}
```

Creación de extensiones para Nette DI

La generación del contenedor DI, además de los archivos de configuración, también está influenciada por las llamadas extensiones. Las activamos en el archivo de configuración en la sección extensions.

Así es como agregamos una extensión representada por la clase BlogExtension bajo el nombre blog:

extensions:
	blog: BlogExtension

Cada extensión del compilador hereda de Nette\DI\CompilerExtension y puede implementar los siguientes métodos, que se llaman secuencialmente durante la construcción del contenedor DI:

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

getConfigSchema()

Este método se llama primero. Define el esquema para validar los parámetros de configuración.

Configuramos la extensión en la sección cuyo nombre es el mismo que aquel bajo el cual se agregó la extensión, es decir, blog:

# mismo nombre que la extensión
blog:
	postsPerPage: 10
	allowComments: false

Creamos un esquema que describe todas las opciones de configuración, incluidos sus tipos, valores permitidos y, opcionalmente, valores predeterminados:

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

Encontrará la documentación en la página Schema. Además, se puede especificar qué opciones pueden ser dinámicas usando dynamic(), por ejemplo, Expect::int()->dynamic().

Accedemos a la configuración a través de la variable $this->config, que es un objeto stdClass:

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

loadConfiguration()

Se utiliza para agregar servicios al contenedor. Para esto se utiliza 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']) // o setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}

La convención es prefijar los servicios agregados por la extensión con su nombre para evitar conflictos de nombres. Esto lo hace el método prefix(), por lo que si la extensión se llama blog, el servicio se llamará blog.articles.

Si necesitamos renombrar un servicio, podemos crear un alias con el nombre original para mantener la compatibilidad hacia atrás. Nette hace algo similar, por ejemplo, con el servicio routing.router, que también está disponible bajo el nombre anterior router.

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

Carga de servicios desde un archivo

No tenemos que crear servicios solo usando la API de la clase ContainerBuilder, sino también con la notación familiar utilizada en el archivo de configuración NEON en la sección de servicios. El prefijo @extension representa la extensión actual.

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

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

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

Cargamos los servicios:

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

		// cargar el archivo de configuración para la extensión
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}

beforeCompile()

El método se llama cuando el contenedor contiene todos los servicios agregados por las extensiones individuales en los métodos loadConfiguration y también por los archivos de configuración del usuario. En esta etapa de construcción, podemos modificar las definiciones de servicios o agregar enlaces entre ellos. Para buscar servicios en el contenedor por tags, se puede usar el método findByTag(), y por clase o interfaz, el método 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()

En esta etapa, la clase del contenedor ya está generada en forma de objeto ClassType, contiene todos los métodos que crean servicios y está lista para ser escrita en la caché. Todavía podemos modificar el código de la clase resultante en este momento.

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

$initialization

La clase Configurator, después de crear el contenedor, llama al código de inicialización, que se crea escribiendo en el objeto $this->initialization usando el método addBody().

Mostraremos un ejemplo de cómo iniciar la sesión con código de inicialización o ejecutar servicios que tienen el tag run:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// inicio automático de la sesión
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// los servicios con el tag run deben crearse después de instanciar el contenedor
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}