Méthodes et attributs inject

Cet article se concentre sur les différentes façons de passer des dépendances aux presenters dans le framework Nette. Nous comparerons la méthode préférée, l'injection par le constructeur, avec des alternatives comme les méthodes et les attributs inject.

Pour les presenters, comme pour les autres classes, passer les dépendances par le constructeur est l'approche préférée. Cependant, si vous créez un ancêtre commun dont héritent les autres presenters (par exemple BasePresenter) et que cet ancêtre a lui aussi besoin de dépendances, un problème appelé constructor hell peut apparaître. On peut le contourner par des méthodes alternatives, à savoir les méthodes et les attributs inject (autrefois des annotations).

Méthodes inject*()

C'est une forme de passage des dépendances par setters. Les noms de ces setters doivent commencer par le préfixe inject. Nette DI appelle automatiquement les méthodes ainsi nommées juste après la création de l'instance du presenter, en leur passant toutes les dépendances requises. Elles doivent donc être déclarées publiques.

Les méthodes inject*() peuvent être vues comme des extensions du constructeur, réparties en plusieurs méthodes. Cela permet à BasePresenter de recevoir ses dépendances par une méthode distincte, en laissant le constructeur libre pour ses descendants :

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
	private Foo $foo;

	public function injectBase(Foo $foo): void
	{
		$this->foo = $foo;
	}
}

class MyPresenter extends BasePresenter
{
	private Bar $bar;

	public function __construct(Bar $bar)
	{
		$this->bar = $bar;
	}
}

Un presenter peut avoir autant de méthodes inject*() que nécessaire, et chacune peut accepter autant de paramètres que nécessaire. Cette approche convient aussi très bien aux cas où un presenter est composé de traits et où chaque trait a besoin de ses propres dépendances.

Attributs Inject

C'est une forme d'injection dans les propriétés. Il suffit de marquer les propriétés à injecter et Nette DI passera automatiquement les dépendances juste après la création de l'instance du presenter. Pour permettre l'injection, ces propriétés doivent être déclarées publiques.

Les propriétés se marquent par un attribut : (auparavant, on utilisait l'annotation /** @inject */)

use Nette\DI\Attributes\Inject;  // cette ligne est importante

class MyPresenter extends Nette\Application\UI\Presenter
{
	#[Inject]
	public Cache $cache;
}

L'avantage de cette façon de passer les dépendances était sa forme d'écriture très concise. Cependant, avec l'arrivée de la promotion des propriétés du constructeur, utiliser le constructeur paraît souvent plus simple.

En revanche, cette méthode souffre des mêmes défauts que l'injection dans les propriétés en général : nous perdons le contrôle sur les changements de la variable, et celle-ci devient partie de l'interface publique de la classe, ce qui est généralement indésirable.