Inject Methods and Attributes

In this article, we will focus on various ways of passing dependencies to presenters in the Nette framework. We will compare the preferred method, which is the constructor, with other options such as inject methods and attributes.

For presenters as well, passing dependencies using the constructor is the preferred way. However, if you create a common ancestor from which other presenters inherit (e.g., BasePresenter), and this ancestor also has dependencies, a problem arises, which we call constructor hell. This can be bypassed using alternative methods, which include inject methods and attributes (annotations).

inject*() Methods

This is a form of dependency passing using setters. The names of these setters begin with the prefix inject. Nette DI automatically calls such named methods immediately after creating the presenter instance and passes all required dependencies to them. They must therefore be declared as public.

inject*() methods can be considered as a kind of constructor extension into multiple methods. Thanks to this, the BasePresenter can take dependencies through another method and leave the constructor free for its descendants:

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

The presenter can contain any number of inject*() methods, and each can have any number of parameters. This is also great for cases where the presenter is composed of traits, and each of them requires its own dependency.

Inject Attributes

This is a form of injection into properties. It is enough to indicate which properties should be injected, and Nette DI automatically passes dependencies immediately after creating the presenter instance. To insert them, it is necessary to declare them as public.

Properties are marked with an attribute: (previously, the annotation /** @inject */ was used)

use Nette\DI\Attributes\Inject; // this line is important

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

The advantage of this method of passing dependencies was its very economical form of notation. However, with the introduction of constructor property promotion, using the constructor seems easier.

On the other hand, this method suffers from the same shortcomings as passing dependencies into properties in general: we have no control over changes in the variable, and at the same time, the variable becomes part of the public interface of the class, which is undesirable.