Nette Documentation Preview

syntax
Autowiring
**********

.[perex]
El autowiring es una gran característica que pasa automáticamente al constructor y a otros métodos los servicios necesarios, de modo que no tenemos que indicarlos explícitamente. Le ahorrará mucho tiempo.

Gracias a él podemos omitir la gran mayoría de los argumentos al escribir las definiciones de los servicios. En lugar de:

```neon
services:
	articles: Model\ArticleRepository(@database, @cache.storage)
```

Basta con escribir:

```neon
services:
	articles: Model\ArticleRepository
```

El autowiring se guía por los tipos, así que para que funcione la clase `ArticleRepository` debe estar definida más o menos así:

```php
namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}
```

El autowiring nunca usa los nombres de los servicios. Se guía únicamente por el sistema de tipos de PHP, así que sabe también que una clase satisface las interfaces que implementa y las clases de las que hereda. Gracias a eso, el nombre de un servicio es solo un identificador auxiliar y renombrarlo no romperá nada en la aplicación.

Para poder usar el autowiring debe haber en el contenedor **exactamente un servicio** de cada tipo. Si hubiera más, el autowiring no sabría cuál pasar y lanzaría una excepción:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANZA UNA EXCEPCIÓN, coinciden mainDb y tempDb
```

Una solución es saltarse el autowiring e indicar explícitamente el nombre del servicio (p. ej. `articles: Model\ArticleRepository(@mainDb)`). Un enfoque más cómodo es, sin embargo, [desactivar |#Desactivar el autowiring] el autowiring para uno de los servicios o [preferir |#Preferencia de autowiring] un servicio sobre los demás.


Desactivar el autowiring
------------------------

Podemos desactivar el autowiring de un servicio con la opción `autowired: false`:

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # el servicio tempDb queda excluido del autowiring

	articles: Model\ArticleRepository  # por eso pasa mainDb al constructor
```

El servicio `articles` no lanzará una excepción por haber dos servicios `PDO` coincidentes (`mainDb` y `tempDb`) disponibles para el constructor, porque solo tiene en cuenta el servicio `mainDb`.

El autowiring también se puede desactivar globalmente para tipos enteros con la opción de configuración [`di › excluded` |configuration#DI], que enumera los tipos (y sus descendientes) que nunca deben autoconectarse.

.[note]
La configuración del autowiring en Nette se diferencia de la de Symfony. En Symfony, `autowire: false` significa que el autowiring no debe usarse para los argumentos del constructor del servicio. En Nette, el autowiring se aplica a los argumentos del constructor y a cualquier otro método invocado a través del contenedor (como la inyección por setter). La opción `autowired: false` impide que el contenedor pase automáticamente esta instancia del servicio como dependencia a otros servicios.


Preferencia de autowiring
-------------------------

Si tenemos varios servicios del mismo tipo e indicamos la opción `autowired` en uno de ellos, ese servicio pasa a ser el preferido:

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # pasa a ser el preferido

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository
```

El servicio `articles` no lanzará una excepción por haber varios servicios `PDO` coincidentes (`mainDb` y `tempDb`), sino que usará el preferido, que es `mainDb`.


Colección de servicios
----------------------

El autowiring también puede pasar arrays de servicios de un tipo concreto. Como PHP no admite de forma nativa indicar el tipo de los elementos de un array en los type hints, hay que complementar el type hint `array` con un comentario phpDoc que indique el tipo de los elementos, del estilo `ClassName[]`:

```php
namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}
```

El contenedor DI pasa entonces automáticamente un array de los servicios que corresponden al tipo indicado. Omite los servicios que tienen el [autowiring desactivado |#Desactivar el autowiring] y nunca incluye en su propia colección el servicio que se está creando. A diferencia de lo que ocurre al pasar un servicio individual, aquí no tienen ningún efecto el [estrechamiento |#Estrechar el autowiring] del autowiring a un tipo concreto ni marcar un servicio como [preferido |#Preferencia de autowiring]: el array contiene siempre todos los servicios del tipo dado.

El tipo del comentario también puede tener la forma `array<int, Class>` o `list<Class>`. Si no puede controlar la forma del comentario phpDoc, puede pasar el array de servicios directamente en la configuración con [`typed()` |services#Funciones especiales].


Argumentos escalares
--------------------

El autowiring solo funciona para objetos y arrays de objetos. Los argumentos escalares (p. ej. cadenas, números, booleanos) hay que [indicarlos en la configuración |services#Argumentos]. Una alternativa es crear un [objeto de ajustes|best-practices:passing-settings-to-presenters] que encapsule el valor escalar (o varios valores). Ese objeto se puede pasar después mediante autowiring.

```php
class MySettings
{
	public function __construct(
		// readonly se puede usar desde PHP 8.1
		public readonly bool $value,
	)
	{}
}
```

Lo registra como servicio añadiéndolo a la configuración:

```neon
services:
	- MySettings('any value')
```

Las demás clases pueden pedirlo entonces mediante autowiring.


Dependencias opcionales
-----------------------

Si un parámetro del constructor o de un método tiene valor por defecto y en el contenedor no existe ningún servicio del tipo requerido, el autowiring no lanza una excepción: simplemente se salta el argumento, así que se usa el valor por defecto. Así se declaran las dependencias opcionales:

```php
class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}
```

En cambio, en un parámetro sin valor por defecto, la ausencia del servicio provoca siempre una excepción.


Estrechar el autowiring
-----------------------

En los servicios concretos, el autowiring se puede estrechar a clases o interfaces determinadas.

Normalmente, el autowiring pasa un servicio a todos los parámetros de método cuyo tipo coincida con el del servicio. Estrechar significa que establecemos condiciones que los tipos indicados en los parámetros de los métodos deben cumplir para que se les pase el servicio.

Veamos un ejemplo:

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Si los registráramos todos como servicios, el autowiring fallaría:

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANZA UNA EXCEPCIÓN, coinciden los servicios parent y child
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor
```

El servicio `parentDep` lanza la excepción `Multiple services of type ParentClass found: child, parent`, porque tanto el servicio `parent` como el `child` encajan en su constructor y el autowiring no puede decidir cuál elegir.

Para el servicio `child` podemos, por tanto, estrechar su autowiring al tipo `ChildClass`:

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # también se puede escribir como 'autowired: self'

	parentDep: ParentDependent  # el autowiring pasa el servicio parent al constructor
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor
```

Ahora, al constructor del servicio `parentDep` se le pasa el servicio `parent`, porque es el único objeto coincidente. El servicio `child` ya no se le pasa por autowiring. Sí, el servicio `child` sigue siendo del tipo `ParentClass`, pero la condición de estrechamiento `autowired: ChildClass` significa que solo se pasará a parámetros tipados explícitamente como `ChildClass` (o sus subtipos). Como `ParentDependent` requiere `ParentClass`, el servicio `child` ya no se considera candidato para el autowiring ahí.

Para el servicio `child`, `autowired: ChildClass` también se podría escribir como `autowired: self`, porque `self` es un marcador de posición para la clase del servicio actual.

En la clave `autowired` también es posible indicar varias clases o interfaces como array:

```neon
autowired: [ParentClass, FooInterface]
```

Probemos a añadir interfaces al ejemplo:

```php
interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

Si no restringimos el servicio `child` de ninguna manera, encajará en los constructores de todas las clases `FooDependent`, `BarDependent`, `ParentDependent` y `ChildDependent`, y el autowiring lo pasará a todos.

Sin embargo, si estrechamos su autowiring a `ChildClass` con `autowired: ChildClass` (o `self`), el autowiring lo pasará solo al constructor de `ChildDependent`, porque este requiere un argumento del tipo `ChildClass` y se cumple que `ChildClass` *es del tipo* `ChildClass`. Ninguno de los tipos requeridos por los demás parámetros es `ChildClass` ni un subtipo suyo, así que el servicio no se les pasa.

Si lo restringimos a `ParentClass` con `autowired: ParentClass`, el autowiring lo pasará de nuevo al constructor de `ChildDependent` (porque el `ChildClass` requerido es un subtipo de `ParentClass`) y ahora también al constructor de `ParentDependent`, porque el tipo requerido `ParentClass` también es adecuado.

Si lo restringimos a `FooInterface`, se seguirá autoconectando a `ParentDependent` (el `ParentClass` requerido es un subtipo de `FooInterface`) y a `ChildDependent`, y además al constructor de `FooDependent`, pero no a `BarDependent`, porque `BarInterface` no es un subtipo de `FooInterface`.

```neon
services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # el autowiring pasa el servicio child al constructor
	barDep: BarDependent        # LANZA UNA EXCEPCIÓN, no coincide ningún servicio
	parentDep: ParentDependent  # el autowiring pasa el servicio child al constructor
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor
```

Autowiring

El autowiring es una gran característica que pasa automáticamente al constructor y a otros métodos los servicios necesarios, de modo que no tenemos que indicarlos explícitamente. Le ahorrará mucho tiempo.

Gracias a él podemos omitir la gran mayoría de los argumentos al escribir las definiciones de los servicios. En lugar de:

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

Basta con escribir:

services:
	articles: Model\ArticleRepository

El autowiring se guía por los tipos, así que para que funcione la clase ArticleRepository debe estar definida más o menos así:

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

El autowiring nunca usa los nombres de los servicios. Se guía únicamente por el sistema de tipos de PHP, así que sabe también que una clase satisface las interfaces que implementa y las clases de las que hereda. Gracias a eso, el nombre de un servicio es solo un identificador auxiliar y renombrarlo no romperá nada en la aplicación.

Para poder usar el autowiring debe haber en el contenedor exactamente un servicio de cada tipo. Si hubiera más, el autowiring no sabría cuál pasar y lanzaría una excepción:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # LANZA UNA EXCEPCIÓN, coinciden mainDb y tempDb

Una solución es saltarse el autowiring e indicar explícitamente el nombre del servicio (p. ej. articles: Model\ArticleRepository(@mainDb)). Un enfoque más cómodo es, sin embargo, desactivar el autowiring para uno de los servicios o preferir un servicio sobre los demás.

Desactivar el autowiring

Podemos desactivar el autowiring de un servicio con la opción autowired: false:

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # el servicio tempDb queda excluido del autowiring

	articles: Model\ArticleRepository  # por eso pasa mainDb al constructor

El servicio articles no lanzará una excepción por haber dos servicios PDO coincidentes (mainDb y tempDb) disponibles para el constructor, porque solo tiene en cuenta el servicio mainDb.

El autowiring también se puede desactivar globalmente para tipos enteros con la opción de configuración di › excluded, que enumera los tipos (y sus descendientes) que nunca deben autoconectarse.

La configuración del autowiring en Nette se diferencia de la de Symfony. En Symfony, autowire: false significa que el autowiring no debe usarse para los argumentos del constructor del servicio. En Nette, el autowiring se aplica a los argumentos del constructor y a cualquier otro método invocado a través del contenedor (como la inyección por setter). La opción autowired: false impide que el contenedor pase automáticamente esta instancia del servicio como dependencia a otros servicios.

Preferencia de autowiring

Si tenemos varios servicios del mismo tipo e indicamos la opción autowired en uno de ellos, ese servicio pasa a ser el preferido:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # pasa a ser el preferido

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

El servicio articles no lanzará una excepción por haber varios servicios PDO coincidentes (mainDb y tempDb), sino que usará el preferido, que es mainDb.

Colección de servicios

El autowiring también puede pasar arrays de servicios de un tipo concreto. Como PHP no admite de forma nativa indicar el tipo de los elementos de un array en los type hints, hay que complementar el type hint array con un comentario phpDoc que indique el tipo de los elementos, del estilo ClassName[]:

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

El contenedor DI pasa entonces automáticamente un array de los servicios que corresponden al tipo indicado. Omite los servicios que tienen el autowiring desactivado y nunca incluye en su propia colección el servicio que se está creando. A diferencia de lo que ocurre al pasar un servicio individual, aquí no tienen ningún efecto el estrechamiento del autowiring a un tipo concreto ni marcar un servicio como preferido: el array contiene siempre todos los servicios del tipo dado.

El tipo del comentario también puede tener la forma array<int, Class> o list<Class>. Si no puede controlar la forma del comentario phpDoc, puede pasar el array de servicios directamente en la configuración con typed().

Argumentos escalares

El autowiring solo funciona para objetos y arrays de objetos. Los argumentos escalares (p. ej. cadenas, números, booleanos) hay que indicarlos en la configuración. Una alternativa es crear un objeto de ajustes que encapsule el valor escalar (o varios valores). Ese objeto se puede pasar después mediante autowiring.

class MySettings
{
	public function __construct(
		// readonly se puede usar desde PHP 8.1
		public readonly bool $value,
	)
	{}
}

Lo registra como servicio añadiéndolo a la configuración:

services:
	- MySettings('any value')

Las demás clases pueden pedirlo entonces mediante autowiring.

Dependencias opcionales

Si un parámetro del constructor o de un método tiene valor por defecto y en el contenedor no existe ningún servicio del tipo requerido, el autowiring no lanza una excepción: simplemente se salta el argumento, así que se usa el valor por defecto. Así se declaran las dependencias opcionales:

class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}

En cambio, en un parámetro sin valor por defecto, la ausencia del servicio provoca siempre una excepción.

Estrechar el autowiring

En los servicios concretos, el autowiring se puede estrechar a clases o interfaces determinadas.

Normalmente, el autowiring pasa un servicio a todos los parámetros de método cuyo tipo coincida con el del servicio. Estrechar significa que establecemos condiciones que los tipos indicados en los parámetros de los métodos deben cumplir para que se les pase el servicio.

Veamos un ejemplo:

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Si los registráramos todos como servicios, el autowiring fallaría:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # LANZA UNA EXCEPCIÓN, coinciden los servicios parent y child
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor

El servicio parentDep lanza la excepción Multiple services of type ParentClass found: child, parent, porque tanto el servicio parent como el child encajan en su constructor y el autowiring no puede decidir cuál elegir.

Para el servicio child podemos, por tanto, estrechar su autowiring al tipo ChildClass:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # también se puede escribir como 'autowired: self'

	parentDep: ParentDependent  # el autowiring pasa el servicio parent al constructor
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor

Ahora, al constructor del servicio parentDep se le pasa el servicio parent, porque es el único objeto coincidente. El servicio child ya no se le pasa por autowiring. Sí, el servicio child sigue siendo del tipo ParentClass, pero la condición de estrechamiento autowired: ChildClass significa que solo se pasará a parámetros tipados explícitamente como ChildClass (o sus subtipos). Como ParentDependent requiere ParentClass, el servicio child ya no se considera candidato para el autowiring ahí.

Para el servicio child, autowired: ChildClass también se podría escribir como autowired: self, porque self es un marcador de posición para la clase del servicio actual.

En la clave autowired también es posible indicar varias clases o interfaces como array:

autowired: [ParentClass, FooInterface]

Probemos a añadir interfaces al ejemplo:

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Si no restringimos el servicio child de ninguna manera, encajará en los constructores de todas las clases FooDependent, BarDependent, ParentDependent y ChildDependent, y el autowiring lo pasará a todos.

Sin embargo, si estrechamos su autowiring a ChildClass con autowired: ChildClass (o self), el autowiring lo pasará solo al constructor de ChildDependent, porque este requiere un argumento del tipo ChildClass y se cumple que ChildClass es del tipo ChildClass. Ninguno de los tipos requeridos por los demás parámetros es ChildClass ni un subtipo suyo, así que el servicio no se les pasa.

Si lo restringimos a ParentClass con autowired: ParentClass, el autowiring lo pasará de nuevo al constructor de ChildDependent (porque el ChildClass requerido es un subtipo de ParentClass) y ahora también al constructor de ParentDependent, porque el tipo requerido ParentClass también es adecuado.

Si lo restringimos a FooInterface, se seguirá autoconectando a ParentDependent (el ParentClass requerido es un subtipo de FooInterface) y a ChildDependent, y además al constructor de FooDependent, pero no a BarDependent, porque BarInterface no es un subtipo de FooInterface.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # el autowiring pasa el servicio child al constructor
	barDep: BarDependent        # LANZA UNA EXCEPCIÓN, no coincide ningún servicio
	parentDep: ParentDependent  # el autowiring pasa el servicio child al constructor
	childDep: ChildDependent    # el autowiring pasa el servicio child al constructor