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

この受け渡し方法の利点は、構文がとても簡潔なことでした。しかしコンストラクタのプロパティ昇格が導入されてからは、コンストラクタを使うほうが簡単に見えることが多くなりました。

逆にこの方法は、プロパティへの注入一般と同じ欠点を抱えています。変数の変更を制御できず、その変数がクラスの公開インターフェースの一部になってしまいます。これは一般に望ましくありません。