Usines générées

Nette DI peut générer automatiquement du code d'usine basé sur l'interface, ce qui vous évite d'écrire du code.

Une fabrique est une classe qui crée et configure des objets. Elle leur transmet donc également leurs dépendances. Ne pas confondre avec le modèle de conception méthode usine, qui décrit une manière spécifique d'utiliser les usines et n'est pas lié à ce sujet.

Nous avons montré à quoi ressemble une telle usine dans le chapitre d'introduction:

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

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

Nette DI peut générer du code de fabrique automatiquement. Tout ce que vous avez à faire est de créer une interface et Nette DI va générer une implémentation. L'interface doit avoir exactement une méthode nommée create et déclarer un type de retour :

interface ArticleFactory
{
	function create(): Article;
}

Ainsi, la fabrique ArticleFactory a une méthode create qui crée des objets Article. La classe Article pourrait ressembler à ce qui suit, par exemple :

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

Ajoutez la fabrique au fichier de configuration :

services:
	- ArticleFactory

Nette DI va générer l'implémentation de la fabrique correspondante.

Ainsi, dans le code qui utilise la fabrique, nous demandons l'objet par interface et Nette DI utilise l'implémentation générée :

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

	public function foo()
	{
		// laissez la fabrique créer un objet
		$article = $this->articleFactory->create();
	}
}

Usine paramétrée

La méthode factory create peut accepter des paramètres qu'elle transmet ensuite au constructeur. Par exemple, ajoutons un ID d'auteur d'article à la classe Article:

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

Nous allons également ajouter le paramètre à la fabrique :

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

Comme le paramètre dans le constructeur et le paramètre dans la fabrique ont le même nom, Nette DI les passera automatiquement.

Définition avancée

La définition peut également être écrite sous forme de lignes multiples à l'aide de la touche implement:

services:
	articleFactory:
		implement: ArticleFactory

Lorsqu'on écrit de cette manière plus longue, il est possible de fournir des arguments supplémentaires pour le constructeur dans la clé arguments et une configuration supplémentaire en utilisant setup, comme pour les services normaux.

Exemple : si la méthode create() n'accepte pas le paramètre $authorId, nous pouvons spécifier une valeur fixe dans la configuration qui sera transmise au constructeur Article:

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

Ou, à l'inverse, si create() acceptait le paramètre $authorId mais qu'il ne faisait pas partie du constructeur et était transmis par la méthode Article::setAuthorId(), nous y ferions référence dans la section setup:

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

Accesseur

Outre les fabriques, Nette peut également générer ce que l'on appelle des accesseurs. L'accesseur est un objet avec une méthode get() qui retourne un service particulier du conteneur DI. Les appels multiples à get() renverront toujours la même instance.

Les accesseurs apportent un chargement paresseux aux dépendances. Prenons l'exemple d'une classe qui enregistre les erreurs dans une base de données spéciale. Si la connexion à la base de données était transmise en tant que dépendance dans son constructeur, la connexion devrait toujours être créée, bien qu'elle ne soit utilisée que rarement lorsqu'une erreur apparaît, de sorte qu'elle resterait le plus souvent inutilisée. Au lieu de cela, la classe peut passer un accesseur et lorsque sa méthode get() est appelée, c'est seulement à ce moment-là que l'objet base de données est créé :

Comment créer un accesseur ? Ecrivez seulement une interface et Nette DI générera l'implémentation. L'interface doit avoir exactement une méthode appelée get et doit déclarer le type de retour :

interface PDOAccessor
{
	function get(): PDO;
}

Ajoutez l'accesseur au fichier de configuration avec la définition du service que l'accesseur retournera :

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

L'accesseur retourne un service de type PDO et comme il n'y a qu'un seul service de ce type dans la configuration, l'accesseur le retournera. Avec plusieurs services configurés de ce type, vous pouvez spécifier celui qui doit être retourné en utilisant son nom, par exemple - PDOAccessor(@db1).

Multifactory/Accesseur

Jusqu'à présent, les fabriques et les accesseurs ne pouvaient créer ou renvoyer qu'un seul objet. Il est également possible de créer une classe multifactory combinée à un accesseur. L'interface d'une telle classe multifactory peut être constituée de plusieurs méthodes appelées create<name>() et get<name>()par exemple :

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

Au lieu de passer plusieurs fabriques et accesseurs générés, vous pouvez passer une seule multifactory complexe.

Vous pouvez également utiliser get() avec un paramètre au lieu de plusieurs méthodes :

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

Dans ce cas, MultiFactory::getArticle() fait la même chose que MultiFactoryAlt::get('article'). Cependant, la syntaxe alternative présente quelques inconvénients. Les valeurs $name prises en charge ne sont pas claires et le type de retour ne peut pas être spécifié dans l'interface en cas d'utilisation de plusieurs valeurs $name différentes.

Définition avec une liste

Cette méthode peut être utilisée pour définir une usine multiple dans la configuration :

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

Ou bien, dans la définition de la fabrique, nous pouvons nous référer à des services existants en utilisant une référence :

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

Définition avec balises

Une autre option pour définir un multifactory est d'utiliser des balises:

services:
	- App\Router\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
version: 3.x