Metody i atrybuty inject

Ten artykuł skupia się na różnych sposobach przekazywania zależności do presenterów we frameworku Nette. Porównamy preferowany sposób, czyli wstrzykiwanie przez konstruktor, z alternatywami w postaci metod i atrybutów inject.

W przypadku presenterów, podobnie jak w innych klasach, preferowanym sposobem przekazywania zależności jest konstruktor. Jeśli jednak tworzysz wspólnego przodka, po którym dziedziczą pozostałe presentery (np. BasePresenter), i ten przodek również potrzebuje zależności, może pojawić się problem zwany constructor hell. Da się go obejść za pomocą alternatywnych sposobów, czyli metod i atrybutów inject (dawniej adnotacji).

Metody inject*()

Jest to forma przekazywania zależności przez settery. Nazwy tych setterów muszą zaczynać się od przedrostka inject. Nette DI wywołuje tak nazwane metody automatycznie zaraz po utworzeniu instancji presentera i przekazuje im wszystkie potrzebne zależności. Dlatego muszą być zadeklarowane jako public.

Metody inject*() można traktować jako rozszerzenie konstruktora rozbite na kilka metod. Dzięki temu BasePresenter może przyjąć zależności osobną metodą i pozostawić konstruktor do dyspozycji swoim potomkom:

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;
	}
}

Presenter może mieć dowolną liczbę metod inject*(), a każda z nich może przyjmować dowolną liczbę parametrów. Ten sposób świetnie sprawdza się także wtedy, gdy presenter jest składany z traitów, a każdy trait wymaga własnych zależności.

Atrybuty Inject

Jest to forma wstrzykiwania do właściwości. Wystarczy oznaczyć właściwości, które mają zostać wstrzyknięte, a Nette DI przekaże zależności automatycznie zaraz po utworzeniu instancji presentera. Aby wstrzyknięcie było możliwe, właściwości te muszą być zadeklarowane jako public.

Właściwości oznaczamy atrybutem: (wcześniej używano adnotacji /** @inject */)

use Nette\DI\Attributes\Inject;  // ta linia jest ważna

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

Zaletą tego sposobu przekazywania zależności była bardzo zwięzła składnia. Odkąd jednak istnieje constructor property promotion, użycie konstruktora często wydaje się prostsze.

Z drugiej strony ten sposób cierpi na te same wady co wstrzykiwanie do właściwości w ogóle: nie mamy kontroli nad zmianami zmiennej, a sama zmienna staje się częścią publicznego interfejsu klasy, co jest generalnie niepożądane.