Metodi e attributi inject

Questo articolo si concentra sui vari modi di passare le dipendenze ai presenter nel framework Nette. Confronteremo il metodo preferito, l'iniezione tramite costruttore, con le alternative, cioè i metodi e gli attributi inject.

Per i presenter, come per le altre classi, il modo preferito di passare le dipendenze è tramite il costruttore. Se però create un antenato comune dal quale gli altri presenter ereditano (per esempio BasePresenter) e anche questo antenato richiede delle dipendenze, può nascere un problema noto come inferno dei costruttori. Lo si può aggirare con metodi alternativi, cioè con i metodi e gli attributi inject (in passato annotazioni).

Metodi inject*()

È una forma di passaggio delle dipendenze tramite setter. I nomi di questi setter devono iniziare con il prefisso inject. Nette DI chiama automaticamente i metodi con questo nome subito dopo aver creato l'istanza del presenter, passando loro tutte le dipendenze necessarie. Devono quindi essere dichiarati pubblici.

I metodi inject*() si possono vedere come estensioni del costruttore, suddivise in più metodi. Questo permette a BasePresenter di ricevere le proprie dipendenze con un metodo separato, lasciando libero il costruttore per i suoi discendenti:

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

Un presenter può avere un numero qualsiasi di metodi inject*(), e ognuno può accettare un numero qualsiasi di parametri. Questo approccio si presta bene anche ai casi in cui un presenter è composto da trait e ogni trait richiede le proprie dipendenze.

Attributi Inject

È una forma di iniezione nelle proprietà. Basta contrassegnare le proprietà da iniettare e Nette DI passerà automaticamente le dipendenze subito dopo aver creato l'istanza del presenter. Perché l'iniezione sia possibile, queste proprietà devono essere dichiarate pubbliche.

Le proprietà si contrassegnano con un attributo: (in passato si usava l'annotazione /** @inject */)

use Nette\DI\Attributes\Inject;  // questa riga è importante

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

Il vantaggio di questo modo di passare le dipendenze era la sintassi molto concisa. Con l'introduzione della constructor property promotion, però, usare il costruttore appare spesso più semplice.

Al contrario, questo metodo soffre degli stessi difetti dell'iniezione nelle proprietà in generale: ci manca il controllo sulle modifiche della variabile e la variabile entra a far parte dell'interfaccia pubblica della classe, cosa in generale indesiderabile.