Nette Documentation Preview

syntax
Методы и атрибуты 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;
}

Преимуществом этого способа передачи зависимостей была очень краткая запись. Однако с появлением продвижения свойств конструктора использование конструктора часто выглядит проще.

С другой стороны, этот способ страдает теми же недостатками, что и внедрение в свойства вообще: у нас нет контроля над изменениями переменной, а сама переменная становится частью публичного интерфейса класса, что обычно нежелательно.