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\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)