Metody i atrybuty inject

W tym artykule skupimy się na różnych sposobach przekazywania zależności do presenterów w frameworku Nette. Porównamy preferowany sposób, którym jest konstruktor, z innymi możliwościami, takimi jak metody i atrybuty inject.

Również dla presenterów obowiązuje zasada, że przekazywanie zależności za pomocą konstruktora jest preferowaną ścieżką. Jeśli jednak tworzysz wspólnego przodka, z którego dziedziczą inne presentery (np. BasePresenter), i ten przodek również ma zależności, pojawia się problem, który nazywamy constructor hell. Można go obejść za pomocą alternatywnych ścieżek, które stanowią metody i atrybuty (dawniej adnotacje) inject.

Metody inject*()

Jest to forma przekazywania zależności przez setter. Nazwa tych setterów zaczyna się prefiksem inject. Nette DI automatycznie wywołuje tak nazwane metody zaraz po utworzeniu instancji presentera i przekazuje im wszystkie wymagane zależności. Muszą być zatem zadeklarowane jako public.

Metody inject*() można uznać za pewnego rodzaju rozszerzenie konstruktora na wiele metod. Dzięki temu BasePresenter może przyjąć zależności przez inną metodę i pozostawić konstruktor wolny dla swoich potomków:

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

Metod inject*() presenter może zawierać dowolną liczbę, a każda może mieć dowolną liczbę parametrów. Świetnie sprawdzają się również w przypadkach, gdy presenter jest złożony z traitów, a każdy z nich wymaga własnej zależności.

Atrybuty Inject

Jest to forma wstrzykiwania do właściwości. Wystarczy oznaczyć, do których zmiennych ma nastąpić wstrzyknięcie, a Nette DI automatycznie przekaże zależności zaraz po utworzeniu instancji presentera. Aby mógł je wstawić, konieczne jest zadeklarowanie ich 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 Nette\Caching\Cache $cache; // Zmiana typu na Nette\Caching\Cache dla spójności z Cache
}

Zaletą tego sposobu przekazywania zależności była bardzo oszczędna forma zapisu. Jednak wraz z pojawieniem się constructor property promotion wydaje się łatwiejsze użycie konstruktora.

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