Fábricas generadas

Nette DI puede generar automáticamente código de fábrica basado en la interfaz, lo que te ahorra escribir código.

Una fábrica es una clase que crea y configura objetos. Por lo tanto, también les pasa sus dependencias. Por favor, no confundir con el patrón de diseño método de fábrica, que describe una forma específica de utilizar las fábricas y no está relacionado con este tema.

Hemos mostrado cómo es una fábrica de este tipo en el capítulo introductorio:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

Nette DI puede generar código de fábrica automáticamente. Todo lo que tiene que hacer es crear una interfaz y Nette DI generará una implementación. La interfaz debe tener exactamente un método llamado create y declarar un tipo de retorno:

interface ArticleFactory
{
	function create(): Article;
}

Así que la fábrica ArticleFactory tiene un método create que crea objetos Article. La clase Article puede tener, por ejemplo, el siguiente aspecto:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

Añade la factoría al fichero de configuración:

services:
	- ArticleFactory

Nette DI generará la implementación de la fábrica correspondiente.

Así, en el código que utiliza la fábrica, solicitamos el objeto por interfaz y Nette DI utiliza la implementación generada:

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// let the factory create an object
		$article = $this->articleFactory->create();
	}
}

Fábrica parametrizada

El método de fábrica create puede aceptar parámetros que luego pasa al constructor. Por ejemplo, vamos a añadir un ID de autor de artículo a la clase Article:

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

También añadiremos el parámetro a la fábrica:

interface ArticleFactory
{
	function create(int $authorId): Article;
}

Como el parámetro en el constructor y el parámetro en la fábrica tienen el mismo nombre, Nette DI los pasará automáticamente.

Definición avanzada

La definición también se puede escribir en forma multilínea utilizando la clave implement:

services:
	articleFactory:
		implement: ArticleFactory

Cuando se escribe de esta forma más larga, es posible proporcionar argumentos adicionales para el constructor en la clave arguments y configuración adicional usando setup, igual que para los servicios normales.

Ejemplo: si el método create() no aceptara el parámetro $authorId, podríamos especificar un valor fijo en la configuración que se pasaría al constructor Article:

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

O, por el contrario, si create() aceptara el parámetro $authorId pero no formara parte del constructor y fuera pasado por el método Article::setAuthorId(), haríamos referencia a él en la sección setup:

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

Accessor

Además de las fábricas, Nette también puede generar los llamados accessors. Un accessor es un objeto con el método get() que devuelve un servicio concreto del contenedor DI. Múltiples llamadas a get() siempre devolverán la misma instancia.

Accessors trae lazy-loading a las dependencias. Tengamos una clase que registra errores en una base de datos especial. Si la conexión a la base de datos se pasara como una dependencia en su constructor, la conexión tendría que crearse siempre, aunque sólo se utilizaría en raras ocasiones cuando apareciera un error, por lo que la conexión no se utilizaría. En su lugar, la clase puede pasar un accessor y cuando se llame a su método get(), sólo entonces se creará el objeto de la base de datos:

¿Cómo crear un accessor? Escriba sólo una interfaz y Nette DI generará la implementación. La interfaz debe tener exactamente un método llamado get y debe declarar el tipo de retorno:

interface PDOAccessor
{
	function get(): PDO;
}

Añade el accessor al fichero de configuración junto con la definición del servicio que devolverá el accessor:

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

El accesor devuelve un servicio de tipo PDO y como sólo hay un servicio de este tipo en la configuración, el accesor lo devolverá. Con múltiples servicios configurados de ese tipo puedes especificar cuál debe ser devuelto usando su nombre, por ejemplo - PDOAccessor(@db1).

Multifactory/Accessor

Hasta ahora, los factories (fábricas) y los accessors sólo podían crear o devolver un único objeto. También se puede crear un multifactory combinado con un accessor. La interfaz de esta clase multifactory puede consistir en múltiples métodos llamados create<name>() y get<name>(), por ejemplo:

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

En lugar de pasar múltiples fábricas y accessors generados, puedes pasar un único multifactory complejo.

También puede utilizar get() con un parámetro en lugar de varios métodos:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

En este caso, MultiFactory::getArticle() hace lo mismo que MultiFactoryAlt::get('article'). Sin embargo, la sintaxis alternativa tiene algunas desventajas. No está claro qué valores de $name son compatibles y el tipo de retorno no se puede especificar en la interfaz cuando se utilizan varios valores diferentes de $name.

Definición con una lista

De esta forma se puede definir una fábrica múltiple en la configuración:

services:
	- MultiFactory(
		article: Article                      # defines createArticle()
		db: PDO(%dsn%, %user%, %password%)    # defines getDb()
	)

O bien, en la definición de la fábrica, podemos hacer referencia a los servicios existentes utilizando una referencia:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # defines createArticle()
		db: @\PDO            # defines getDb()
	)

Definición con etiquetas

Otra opción para definir un multifactory es utilizar tags:

services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
versión: 3.x