Методи та атрибути inject

У цій статті ми розглянемо різні способи передачі залежностей у презентери у фреймворку Nette. Ми порівняємо бажаний спосіб, яким є конструктор, з іншими варіантами, такими як методи та атрибути inject.

Навіть для презентерів передача залежностей за допомогою конструктора є бажаним шляхом. Однак, якщо ви створюєте спільного предка, від якого успадковуються інші презентери (наприклад, BasePresenter), і цей предок також має залежності, виникає проблема, яку ми називаємо constructor hell. Її можна обійти за допомогою альтернативних шляхів, якими є методи та атрибути (анотації) inject.

Методи inject*()

Це форма передачі залежності сеттером. Назва цих сеттерів починається з префікса inject. Nette DI автоматично викликає методи з такою назвою одразу після створення екземпляра презентера та передає їм усі необхідні залежності. Тому вони повинні бути оголошені як public.

Методи inject*() можна вважати своєрідним розширенням конструктора на кілька методів. Завдяки цьому BasePresenter може приймати залежності через інший метод і залишати конструктор вільним для своїх нащадків:

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

Презентер може містити будь-яку кількість методів inject*(), і кожен може мати будь-яку кількість параметрів. Це також чудово підходить у випадках, коли презентер складається з трейтів, і кожен з них вимагає власної залежності.

Атрибути Inject

Це форма ін'єкції у властивість. Достатньо позначити, в які змінні слід ін'єктувати, і Nette DI автоматично передасть залежності одразу після створення екземпляра презентера. Щоб їх можна було вставити, необхідно оголосити їх як public.

Властивості позначимо атрибутом: (раніше використовувалася анотація /** @inject */)

use Nette\DI\Attributes\Inject;  // цей рядок важливий

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

Перевагою цього способу передачі залежностей була дуже лаконічна форма запису. Однак з появою constructor property promotion простіше використовувати конструктор.

Навпаки, цей спосіб страждає тими ж недоліками, що й передача залежності у властивості загалом: ми не маємо контролю над змінами в змінній, і водночас змінна стає частиною публічного інтерфейсу класу, що є небажаним.