Nette Documentation Preview

syntax
オートワイヤリング
**********

.[perex]
オートワイヤリングは、必要なサービスをコンストラクタやほかのメソッドに自動的に渡してくれる素晴らしい機能で、明示的に指定する必要がなくなります。多くの時間を節約できます。

おかげで、サービスの定義を書くときに大部分の引数を省けます。次のように書く代わりに、

```neon
services:
	articles: Model\ArticleRepository(@database, @cache.storage)
```

こう書くだけで済みます。

```neon
services:
	articles: Model\ArticleRepository
```

オートワイヤリングは型に導かれるので、それが働くには `ArticleRepository` クラスがおおよそ次のように定義されている必要があります。

```php
namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}
```

オートワイヤリングはサービスの名前を決して使いません。PHP の型システムだけに導かれるので、クラスが実装するインターフェースや継承するクラスも満たすと分かります。おかげでサービスの名前は補助的な識別子にすぎず、名前を変えてもアプリケーションの何も壊れません。

オートワイヤリングを使うには、コンテナの中に各型の**サービスがちょうどひとつ**なければなりません。複数あると、オートワイヤリングはどれを渡すべきか分からず例外を投げます。

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # 例外を投げます。mainDb と tempDb の両方が当てはまります
```

ひとつの解は、オートワイヤリングを迂回してサービス名を明示的に指定すること(たとえば `articles: Model\ArticleRepository(@mainDb)`)です。とはいえ、いずれかのサービスのオートワイヤリングを[無効にする |#オートワイヤリングの無効化]か、ひとつのサービスをほかより[優先する |#オートワイヤリングの優先]ほうが便利です。


オートワイヤリングの無効化
--------------

`autowired: false` オプションを使うと、サービスのオートワイヤリングを無効にできます。

```neon
services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # tempDb サービスはオートワイヤリングから除かれます

	articles: Model\ArticleRepository  # なのでコンストラクタには mainDb が渡されます
```

`articles` サービスは、コンストラクタに使える `PDO` サービスが 2 つ(`mainDb` と `tempDb`)あるという例外を投げません。`mainDb` サービスだけを考慮するからです。

オートワイヤリングは、[`di › excluded` |configuration#DI]設定オプションを使って型ごとにまとめて無効にすることもできます。ここには、決してオートワイヤリングされるべきでない型(とその子孫)を並べます。

.[note]
Nette のオートワイヤリングの設定は Symfony とは違います。Symfony では `autowire: false` は、そのサービスのコンストラクタの引数にオートワイヤリングを使わない、という意味です。Nette では、オートワイヤリングはコンストラクタの引数と、コンテナ経由で呼ばれるほかのメソッド(セッターインジェクションなど)に当てはまります。`autowired: false` オプションは、そのサービスのインスタンスがほかのサービスの依存関係として自動的に渡されるのをコンテナに禁じます。


オートワイヤリングの優先
-------------

同じ型のサービスが複数あり、そのひとつに `autowired` オプションを指定すると、そのサービスが優先されるようになります。

```neon
services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # 優先されるようになります

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository
```

`articles` サービスは、当てはまる `PDO` サービスが複数(`mainDb` と `tempDb`)あるという例外を投げず、優先されるほう、つまり `mainDb` を使います。


サービスのコレクション
-----------

オートワイヤリングは、特定の型のサービスの配列を渡すこともできます。PHP は型宣言で配列の要素の型を指定できないので、`array` の型宣言に `ClassName[]` のように要素の型を示す phpDoc のコメントを添える必要があります。

```php
namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}
```

すると DI コンテナは、その型に対応するサービスの配列を自動的に渡します。[オートワイヤリングが無効にされた |#オートワイヤリングの無効化]サービスは除かれ、今まさに作られているサービス自身がそのコレクションに含まれることもありません。個々のサービスを渡す場合と違い、オートワイヤリングを特定の型に[絞り込んだり |#オートワイヤリングの絞り込み]、サービスを[優先 |#オートワイヤリングの優先]にしたりしても、ここでは影響がありません。配列には常にその型のすべてのサービスが入ります。

コメントの型は `array<int, Class>` や `list<Class>` の形でもかまいません。phpDoc のコメントの形を自分で決められない場合は、[`typed()` |services#特別な関数]を使って設定でサービスの配列を直接渡せます。


スカラーの引数
-------

オートワイヤリングが働くのはオブジェクトとオブジェクトの配列だけです。スカラーの引数(文字列、数値、真偽値など)は[設定で指定 |services#引数]しなければなりません。別の方法として、そのスカラー値(や複数の値)を包む[設定オブジェクト|best-practices:passing-settings-to-presenters]を作ることもできます。そのオブジェクトはオートワイヤリングで渡せます。

```php
class MySettings
{
	public function __construct(
		// readonly は PHP 8.1 以降で使えます
		public readonly bool $value,
	)
	{}
}
```

設定に足してサービスとして登録します。

```neon
services:
	- MySettings('any value')
```

これでほかのクラスがオートワイヤリングで要求できます。


省略可能な依存関係
---------

コンストラクタやメソッドのパラメータに既定値があり、求める型のサービスがコンテナにない場合、オートワイヤリングは例外を投げず、その引数を単に飛ばすので既定値が使われます。省略可能な依存関係はこうして宣言します。

```php
class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}
```

これに対し、既定値のないパラメータでは、サービスがないと常に例外になります。


オートワイヤリングの絞り込み
--------------

個々のサービスについて、オートワイヤリングを特定のクラスやインターフェースに絞り込めます。

ふつうオートワイヤリングは、そのサービスが型として当てはまるすべてのメソッドのパラメータにサービスを渡します。絞り込みとは、サービスが渡されるためにメソッドのパラメータの型が満たすべき条件を設けることです。

例を見てみましょう。

```php
class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

これらをすべてサービスとして登録すると、オートワイヤリングは失敗します。

```neon
services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # 例外を投げます。parent と child の両方が当てはまります
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します
```

`parentDep` サービスは `Multiple services of type ParentClass found: child, parent` という例外を投げます。`parent` と `child` の両方がそのコンストラクタに当てはまり、オートワイヤリングがどちらを選ぶか決められないからです。

そこで `child` サービスについて、オートワイヤリングを `ChildClass` 型に絞り込めます。

```neon
services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # 'autowired: self' とも書けます

	parentDep: ParentDependent  # オートワイヤリングは parent サービスをコンストラクタに渡します
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します
```

これで `parentDep` サービスのコンストラクタには `parent` サービスが渡されます。当てはまるオブジェクトがそれだけになったからです。`child` サービスはもうそこへオートワイヤリングされません。たしかに `child` サービスは今も `ParentClass` 型ですが、絞り込みの条件 `autowired: ChildClass` により、明示的に `ChildClass`(またはその派生型)と型付けされたパラメータにしか渡されません。`ParentDependent` は `ParentClass` を求めるので、`child` サービスはそこでのオートワイヤリングの候補から外れます。

`child` サービスの `autowired: ChildClass` は `autowired: self` とも書けます。`self` は現在のサービスのクラスを表すプレースホルダーだからです。

`autowired` キーには、複数のクラスやインターフェースを配列として指定することもできます。

```neon
autowired: [ParentClass, FooInterface]
```

例にインターフェースを足してみましょう。

```php
interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}
```

`child` サービスを何も制限しなければ、`FooDependent`、`BarDependent`、`ParentDependent`、`ChildDependent` のすべてのクラスのコンストラクタに当てはまり、オートワイヤリングはそこへ渡します。

しかし `autowired: ChildClass`(または `self`)でオートワイヤリングを `ChildClass` に絞り込むと、渡されるのは `ChildDependent` のコンストラクタだけになります。そこは `ChildClass` 型の引数を求めており、`ChildClass` は `ChildClass` *である*からです。ほかのパラメータが求める型はどれも `ChildClass` でもその派生型でもないので、サービスは渡されません。

`autowired: ParentClass` で `ParentClass` に制限すると、オートワイヤリングは再び `ChildDependent` のコンストラクタに渡し(求められる `ChildClass` は `ParentClass` の派生型だからです)、さらに `ParentDependent` のコンストラクタにも渡します。求められる型 `ParentClass` も適合するからです。

`FooInterface` に制限すると、引き続き `ParentDependent`(求められる `ParentClass` は `FooInterface` の派生型です)と `ChildDependent` にオートワイヤリングされ、加えて `FooDependent` のコンストラクタにも渡されますが、`BarDependent` には渡されません。`BarInterface` は `FooInterface` の派生型ではないからです。

```neon
services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # オートワイヤリングは child サービスをコンストラクタに渡します
	barDep: BarDependent        # 例外を投げます。当てはまるサービスがありません
	parentDep: ParentDependent  # オートワイヤリングは child サービスをコンストラクタに渡します
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します
```

オートワイヤリング

オートワイヤリングは、必要なサービスをコンストラクタやほかのメソッドに自動的に渡してくれる素晴らしい機能で、明示的に指定する必要がなくなります。多くの時間を節約できます。

おかげで、サービスの定義を書くときに大部分の引数を省けます。次のように書く代わりに、

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

こう書くだけで済みます。

services:
	articles: Model\ArticleRepository

オートワイヤリングは型に導かれるので、それが働くには ArticleRepository クラスがおおよそ次のように定義されている必要があります。

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

オートワイヤリングはサービスの名前を決して使いません。PHP の型システムだけに導かれるので、クラスが実装するインターフェースや継承するクラスも満たすと分かります。おかげでサービスの名前は補助的な識別子にすぎず、名前を変えてもアプリケーションの何も壊れません。

オートワイヤリングを使うには、コンテナの中に各型のサービスがちょうどひとつなければなりません。複数あると、オートワイヤリングはどれを渡すべきか分からず例外を投げます。

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository  # 例外を投げます。mainDb と tempDb の両方が当てはまります

ひとつの解は、オートワイヤリングを迂回してサービス名を明示的に指定すること(たとえば articles: Model\ArticleRepository(@mainDb))です。とはいえ、いずれかのサービスのオートワイヤリングを無効にするか、ひとつのサービスをほかより優先するほうが便利です。

オートワイヤリングの無効化

autowired: false オプションを使うと、サービスのオートワイヤリングを無効にできます。

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false               # tempDb サービスはオートワイヤリングから除かれます

	articles: Model\ArticleRepository  # なのでコンストラクタには mainDb が渡されます

articles サービスは、コンストラクタに使える PDO サービスが 2 つ(mainDbtempDb)あるという例外を投げません。mainDb サービスだけを考慮するからです。

オートワイヤリングは、di › excluded設定オプションを使って型ごとにまとめて無効にすることもできます。ここには、決してオートワイヤリングされるべきでない型(とその子孫)を並べます。

Nette のオートワイヤリングの設定は Symfony とは違います。Symfony では autowire: false は、そのサービスのコンストラクタの引数にオートワイヤリングを使わない、という意味です。Nette では、オートワイヤリングはコンストラクタの引数と、コンテナ経由で呼ばれるほかのメソッド(セッターインジェクションなど)に当てはまります。autowired: false オプションは、そのサービスのインスタンスがほかのサービスの依存関係として自動的に渡されるのをコンテナに禁じます。

オートワイヤリングの優先

同じ型のサービスが複数あり、そのひとつに autowired オプションを指定すると、そのサービスが優先されるようになります。

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO    # 優先されるようになります

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

articles サービスは、当てはまる PDO サービスが複数(mainDbtempDb)あるという例外を投げず、優先されるほう、つまり mainDb を使います。

サービスのコレクション

オートワイヤリングは、特定の型のサービスの配列を渡すこともできます。PHP は型宣言で配列の要素の型を指定できないので、array の型宣言に ClassName[] のように要素の型を示す phpDoc のコメントを添える必要があります。

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

すると DI コンテナは、その型に対応するサービスの配列を自動的に渡します。オートワイヤリングが無効にされたサービスは除かれ、今まさに作られているサービス自身がそのコレクションに含まれることもありません。個々のサービスを渡す場合と違い、オートワイヤリングを特定の型に絞り込んだり、サービスを優先にしたりしても、ここでは影響がありません。配列には常にその型のすべてのサービスが入ります。

コメントの型は array<int, Class>list<Class> の形でもかまいません。phpDoc のコメントの形を自分で決められない場合は、typed()を使って設定でサービスの配列を直接渡せます。

スカラーの引数

オートワイヤリングが働くのはオブジェクトとオブジェクトの配列だけです。スカラーの引数(文字列、数値、真偽値など)は設定で指定しなければなりません。別の方法として、そのスカラー値(や複数の値)を包む設定オブジェクトを作ることもできます。そのオブジェクトはオートワイヤリングで渡せます。

class MySettings
{
	public function __construct(
		// readonly は PHP 8.1 以降で使えます
		public readonly bool $value,
	)
	{}
}

設定に足してサービスとして登録します。

services:
	- MySettings('any value')

これでほかのクラスがオートワイヤリングで要求できます。

省略可能な依存関係

コンストラクタやメソッドのパラメータに既定値があり、求める型のサービスがコンテナにない場合、オートワイヤリングは例外を投げず、その引数を単に飛ばすので既定値が使われます。省略可能な依存関係はこうして宣言します。

class Foo
{
	public function __construct(
		private ?Logger $logger = null,
	) {}
}

これに対し、既定値のないパラメータでは、サービスがないと常に例外になります。

オートワイヤリングの絞り込み

個々のサービスについて、オートワイヤリングを特定のクラスやインターフェースに絞り込めます。

ふつうオートワイヤリングは、そのサービスが型として当てはまるすべてのメソッドのパラメータにサービスを渡します。絞り込みとは、サービスが渡されるためにメソッドのパラメータの型が満たすべき条件を設けることです。

例を見てみましょう。

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

これらをすべてサービスとして登録すると、オートワイヤリングは失敗します。

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # 例外を投げます。parent と child の両方が当てはまります
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します

parentDep サービスは Multiple services of type ParentClass found: child, parent という例外を投げます。parentchild の両方がそのコンストラクタに当てはまり、オートワイヤリングがどちらを選ぶか決められないからです。

そこで child サービスについて、オートワイヤリングを ChildClass 型に絞り込めます。

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass   # 'autowired: self' とも書けます

	parentDep: ParentDependent  # オートワイヤリングは parent サービスをコンストラクタに渡します
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します

これで parentDep サービスのコンストラクタには parent サービスが渡されます。当てはまるオブジェクトがそれだけになったからです。child サービスはもうそこへオートワイヤリングされません。たしかに child サービスは今も ParentClass 型ですが、絞り込みの条件 autowired: ChildClass により、明示的に ChildClass(またはその派生型)と型付けされたパラメータにしか渡されません。ParentDependentParentClass を求めるので、child サービスはそこでのオートワイヤリングの候補から外れます。

child サービスの autowired: ChildClassautowired: self とも書けます。self は現在のサービスのクラスを表すプレースホルダーだからです。

autowired キーには、複数のクラスやインターフェースを配列として指定することもできます。

autowired: [ParentClass, FooInterface]

例にインターフェースを足してみましょう。

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

child サービスを何も制限しなければ、FooDependentBarDependentParentDependentChildDependent のすべてのクラスのコンストラクタに当てはまり、オートワイヤリングはそこへ渡します。

しかし autowired: ChildClass(または self)でオートワイヤリングを ChildClass に絞り込むと、渡されるのは ChildDependent のコンストラクタだけになります。そこは ChildClass 型の引数を求めており、ChildClassChildClass であるからです。ほかのパラメータが求める型はどれも ChildClass でもその派生型でもないので、サービスは渡されません。

autowired: ParentClassParentClass に制限すると、オートワイヤリングは再び ChildDependent のコンストラクタに渡し(求められる ChildClassParentClass の派生型だからです)、さらに ParentDependent のコンストラクタにも渡します。求められる型 ParentClass も適合するからです。

FooInterface に制限すると、引き続き ParentDependent(求められる ParentClassFooInterface の派生型です)と ChildDependent にオートワイヤリングされ、加えて FooDependent のコンストラクタにも渡されますが、BarDependent には渡されません。BarInterfaceFooInterface の派生型ではないからです。

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # オートワイヤリングは child サービスをコンストラクタに渡します
	barDep: BarDependent        # 例外を投げます。当てはまるサービスがありません
	parentDep: ParentDependent  # オートワイヤリングは child サービスをコンストラクタに渡します
	childDep: ChildDependent    # オートワイヤリングは child サービスをコンストラクタに渡します