Методы и атрибуты inject
************************
.[perex]
Эта статья посвящена разным способам передачи зависимостей в презентеры фреймворка Nette. Мы сравним предпочтительный способ, внедрение через конструктор, с альтернативами в виде методов и атрибутов `inject`.
Для презентеров, как и для других классов, предпочтительным подходом является передача зависимостей через [конструктор |dependency-injection:passing-dependencies#Внедрение через конструктор]. Однако если вы создаёте общего предка, от которого наследуются другие презентеры (например, `BasePresenter`), и этому предку тоже нужны зависимости, может возникнуть проблема, известная как [ад конструкторов |dependency-injection:passing-dependencies#Ад конструкторов]. Её можно обойти альтернативными способами, а именно методами и атрибутами inject (раньше аннотациями).
Методы `inject*()`
==================
Это разновидность передачи зависимостей через [сеттеры |dependency-injection:passing-dependencies#Внедрение через сеттер]. Имена таких сеттеров должны начинаться с префикса `inject`. Nette DI автоматически вызывает методы с такими именами сразу после создания экземпляра презентера и передаёт им все нужные зависимости. Поэтому они должны быть объявлены как public.
Методы `inject*()` можно воспринимать как расширение конструктора, разбитое на несколько методов. Это позволяет `BasePresenter` получать свои зависимости отдельным методом, оставляя конструктор свободным для его потомков:
```php
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*()`, и каждый может принимать сколько угодно параметров. Такой подход хорошо подходит и для случаев, когда презентер [составлен из трейтов |presenter-traits] и каждому трейту нужны свои зависимости.
Атрибуты `Inject`
=================
Это разновидность [внедрения в свойства |dependency-injection:passing-dependencies#Внедрение в свойство]. Достаточно пометить свойства, в которые нужно внедрить зависимости, и Nette DI автоматически передаст их сразу после создания экземпляра презентера. Чтобы внедрение было возможно, эти свойства должны быть объявлены как public.
Свойства помечаются атрибутом (раньше использовалась аннотация `/** @inject */`):
```php
use Nette\DI\Attributes\Inject; // эта строка важна
class MyPresenter extends Nette\Application\UI\Presenter
{
#[Inject]
public Cache $cache;
}
```
Преимуществом этого способа передачи зависимостей была очень краткая запись. Однако с появлением [продвижения свойств конструктора |https://blog.nette.org/ru/php-8-0-complete-overview-of-news#toc-constructor-property-promotion] использование конструктора часто выглядит проще.
С другой стороны, этот способ страдает теми же недостатками, что и внедрение в свойства вообще: у нас нет контроля над изменениями переменной, а сама переменная становится частью публичного интерфейса класса, что обычно нежелательно.
Методы и атрибуты inject
Эта статья посвящена разным способам передачи зависимостей в
презентеры фреймворка Nette. Мы сравним предпочтительный способ,
внедрение через конструктор, с альтернативами в виде методов и
атрибутов inject.
Для презентеров, как и для других классов, предпочтительным подходом
является передача зависимостей через конструктор.
Однако если вы создаёте общего предка, от которого наследуются другие
презентеры (например, BasePresenter), и этому предку тоже нужны
зависимости, может возникнуть проблема, известная как ад
конструкторов. Её можно обойти альтернативными способами, а именно
методами и атрибутами 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;
}
Преимуществом этого способа передачи зависимостей была очень
краткая запись. Однако с появлением продвижения
свойств конструктора использование конструктора часто
выглядит проще.
С другой стороны, этот способ страдает теми же недостатками, что и
внедрение в свойства вообще: у нас нет контроля над изменениями
переменной, а сама переменная становится частью публичного интерфейса
класса, что обычно нежелательно.