Nette Documentation Preview

syntax
生成されたファクトリー
***********

.[perex]
Nette DIは、インターフェイスを元にファクトリーコードを自動生成することができるので、コードを書く手間が省けます。

ファクトリーは、オブジェクトを作成し、設定するクラスです。したがって、その依存関係も同様に渡します。ファクトリーメソッド*デザインパターンと混同しないように注意してください。

そんな工場の姿を[序章で |introduction#factory]紹介しました:

```php
class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}
```

Nette DIはファクトリーコードを自動生成することができます。インターフェイスを作成するだけで、Nette DIが実装を生成してくれます。インターフェースは、`create` という名前のメソッドを1つだけ持ち、戻り値の型を宣言する必要があります。

```php
interface ArticleFactory
{
	function create(): Article;
}
```

つまり、ファクトリー`ArticleFactory` は、オブジェクトを作成するメソッド`create` を持っています`Article` 。クラス`Article` は、たとえば次のようなものになるでしょう。

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}
```

設定ファイルにファクトリーを追加します。

```neon
services:
	- ArticleFactory
```

Nette DIは、対応するファクトリーの実装を生成します。

このように、ファクトリーを利用するコードでは、インターフェースでオブジェクトを要求し、生成された実装をネットDIが利用します。

```php
class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// let the factory create an object
		$article = $this->articleFactory->create();
	}
}
```


パラメタライズドファクトリー .[#toc-parameterized-factory]
============================================

ファクトリーメソッド`create` はパラメータを受け取ることができ、それをコンストラクタに渡します。例えば、記事の著者IDをクラス`Article` に追加してみましょう。

```php
class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}
```

また、ファクトリーにパラメータを追加します。

```php
interface ArticleFactory
{
	function create(int $authorId): Article;
}
```

コンストラクタのパラメータとファクトリーのパラメータは同じ名前なので、ネットDIは自動的にこれらを渡します。


高度な定義 .[#toc-advanced-definition]
=================================

定義は、キー`implement` を使って複数行で記述することも可能です。

```neon
services:
	articleFactory:
		implement: ArticleFactory
```

この長い書き方では,通常のサービスと同様に,コンストラクタの引数を`arguments` で,設定を`setup` で追加することが可能です.

例:メソッド`create()` がパラメータ`$authorId` を受け付けない場合,コンストラクタ`Article` に渡される固定値を設定に指定することができる.

```neon
services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123
```

あるいは逆に、`create()` がパラメータ`$authorId` を受け付けたが、それがコンストラクタの一部ではなく、メソッド`Article::setAuthorId()` から渡された場合、セクション`setup` でそれを参照することになる。

```neon
services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)
```


アクセッサー .[#toc-accessor]
=======================

Netteではファクトリーの他に、いわゆるアクセサーを生成することができます。アクセサーとは、DIコンテナから特定のサービスを返す`get()` メソッドを持つオブジェクトです。`get()` を複数回呼び出すと、常に同じインスタンスが返されます。

アクセッサは、依存関係にレイジーローディングをもたらします。例えば、エラーを特殊なデータベースに記録するクラスがあるとします。もしデータベース接続をコンストラクタの依存関係として渡した場合、 接続は常に作成する必要がありますが、エラーが発生したときにしか使われないので、 接続はほとんど使われないままです。
その代わりに、このクラスはアクセサを渡すことができ、その`get()` メソッドが呼ばれたときだけ、データベースオブジェクトが作成されます。

アクセサを作るには?インターフェイスを書くだけで、ネットDIが実装を生成してくれます。インターフェイスは、`get` というメソッドを1つだけ持ち、戻り値の型を宣言しなければなりません。

```php
interface PDOAccessor
{
	function get(): PDO;
}
```

アクセッサが返すサービスの定義と一緒に、設定ファイルにアクセッサを追加してください。

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

このアクセサは`PDO` というタイプのサービスを返します。 このようなサービスは設定に1つしかないので、アクセサはそれを返します。このタイプのサービスが複数設定されている場合、どのサービスを返すかをその名前を使って指定できます。たとえば、`- PDOAccessor(@db1)` 。


マルチファクトリー/アクセサー .[#toc-multifactory-accessor]
=============================================
今までのファクトリーとアクセッサは、1つのオブジェクトを作成するか返すだけでした。アクセッサと組み合わせたマルチファクトリーも作成することができます。このようなマルチファクトリークラスのインターフェイスは `create<name>()`と `get<name>()`と呼ばれる複数のメソッドで構成されます。

```php
interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}
```

生成された複数のファクトリーとアクセッサを渡す代わりに、複雑なマルチファクトリーを1つだけ渡すことができます。

あるいは、複数のメソッドの代わりに、`get()` をパラメータ付きで使うこともできる:

```php
interface MultiFactoryAlt
{
	function get($name): PDO;
}
```

この場合、`MultiFactory::getArticle()` は`MultiFactoryAlt::get('article')` と同じことをする。 しかし、この代替構文にはいくつかの欠点がある。どの`$name` 値がサポートされているのかが明確でないことと、複数の異なる`$name` 値を使用する場合、インターフェイスで戻り値の型を指定できないことである。


リストを使った定義 .[#toc-definition-with-a-list]
----------------------------------------
この方法は、コンフィギュレーションで複数のファクトリーを定義するのに使える:.{data-version:3.2.0}

```neon
services:
	- MultiFactory(
		article: Article                      # defines createArticle()
		db: PDO(%dsn%, %user%, %password%)    # defines getDb()
	)
```

あるいは、ファクトリーの定義で、参照を使用して既存のサービスを参照できます:

```neon
services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # defines createArticle()
		db: @\PDO            # defines getDb()
	)
```


タグ付き定義 .[#toc-definition-with-tags]
-----------------------------------

マルチファクトリーを定義するもう一つの方法は、[タグを |services#Tags]使うことです。

```neon
services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)
```

生成されたファクトリー

Nette DIは、インターフェイスを元にファクトリーコードを自動生成することができるので、コードを書く手間が省けます。

ファクトリーは、オブジェクトを作成し、設定するクラスです。したがって、その依存関係も同様に渡します。ファクトリーメソッド*デザインパターンと混同しないように注意してください。

そんな工場の姿を序章で紹介しました:

class ArticleFactory
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}

	public function create(): Article
	{
		return new Article($this->db);
	}
}

Nette DIはファクトリーコードを自動生成することができます。インターフェイスを作成するだけで、Nette DIが実装を生成してくれます。インターフェースは、create という名前のメソッドを1つだけ持ち、戻り値の型を宣言する必要があります。

interface ArticleFactory
{
	function create(): Article;
}

つまり、ファクトリーArticleFactory は、オブジェクトを作成するメソッドcreate を持っていますArticle 。クラスArticle は、たとえば次のようなものになるでしょう。

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
	) {
	}
}

設定ファイルにファクトリーを追加します。

services:
	- ArticleFactory

Nette DIは、対応するファクトリーの実装を生成します。

このように、ファクトリーを利用するコードでは、インターフェースでオブジェクトを要求し、生成された実装をネットDIが利用します。

class UserController
{
	public function __construct(
		private ArticleFactory $articleFactory,
	) {
	}

	public function foo()
	{
		// let the factory create an object
		$article = $this->articleFactory->create();
	}
}

パラメタライズドファクトリー

ファクトリーメソッドcreate はパラメータを受け取ることができ、それをコンストラクタに渡します。例えば、記事の著者IDをクラスArticle に追加してみましょう。

class Article
{
	public function __construct(
		private Nette\Database\Connection $db,
		private int $authorId,
	) {
	}
}

また、ファクトリーにパラメータを追加します。

interface ArticleFactory
{
	function create(int $authorId): Article;
}

コンストラクタのパラメータとファクトリーのパラメータは同じ名前なので、ネットDIは自動的にこれらを渡します。

高度な定義

定義は、キーimplement を使って複数行で記述することも可能です。

services:
	articleFactory:
		implement: ArticleFactory

この長い書き方では,通常のサービスと同様に,コンストラクタの引数をarguments で,設定をsetup で追加することが可能です.

例:メソッドcreate() がパラメータ$authorId を受け付けない場合,コンストラクタArticle に渡される固定値を設定に指定することができる.

services:
	articleFactory:
		implement: ArticleFactory
		arguments:
			authorId: 123

あるいは逆に、create() がパラメータ$authorId を受け付けたが、それがコンストラクタの一部ではなく、メソッドArticle::setAuthorId() から渡された場合、セクションsetup でそれを参照することになる。

services:
	articleFactory:
		implement: ArticleFactory
		setup:
			- setAuthorId($authorId)

アクセッサー

Netteではファクトリーの他に、いわゆるアクセサーを生成することができます。アクセサーとは、DIコンテナから特定のサービスを返すget() メソッドを持つオブジェクトです。get() を複数回呼び出すと、常に同じインスタンスが返されます。

アクセッサは、依存関係にレイジーローディングをもたらします。例えば、エラーを特殊なデータベースに記録するクラスがあるとします。もしデータベース接続をコンストラクタの依存関係として渡した場合、 接続は常に作成する必要がありますが、エラーが発生したときにしか使われないので、 接続はほとんど使われないままです。 その代わりに、このクラスはアクセサを渡すことができ、そのget() メソッドが呼ばれたときだけ、データベースオブジェクトが作成されます。

アクセサを作るには?インターフェイスを書くだけで、ネットDIが実装を生成してくれます。インターフェイスは、get というメソッドを1つだけ持ち、戻り値の型を宣言しなければなりません。

interface PDOAccessor
{
	function get(): PDO;
}

アクセッサが返すサービスの定義と一緒に、設定ファイルにアクセッサを追加してください。

services:
	- PDOAccessor
	- PDO(%dsn%, %user%, %password%)

このアクセサはPDO というタイプのサービスを返します。 このようなサービスは設定に1つしかないので、アクセサはそれを返します。このタイプのサービスが複数設定されている場合、どのサービスを返すかをその名前を使って指定できます。たとえば、- PDOAccessor(@db1) 。

マルチファクトリー/アクセサー

今までのファクトリーとアクセッサは、1つのオブジェクトを作成するか返すだけでした。アクセッサと組み合わせたマルチファクトリーも作成することができます。このようなマルチファクトリークラスのインターフェイスは create<name>()get<name>()と呼ばれる複数のメソッドで構成されます。

interface MultiFactory
{
	function createArticle(): Article;
	function getDb(): PDO;
}

生成された複数のファクトリーとアクセッサを渡す代わりに、複雑なマルチファクトリーを1つだけ渡すことができます。

あるいは、複数のメソッドの代わりに、get() をパラメータ付きで使うこともできる:

interface MultiFactoryAlt
{
	function get($name): PDO;
}

この場合、MultiFactory::getArticle()MultiFactoryAlt::get('article') と同じことをする。 しかし、この代替構文にはいくつかの欠点がある。どの$name 値がサポートされているのかが明確でないことと、複数の異なる$name 値を使用する場合、インターフェイスで戻り値の型を指定できないことである。

リストを使った定義

この方法は、コンフィギュレーションで複数のファクトリーを定義するのに使える:.{data-version:3.2.0}

services:
	- MultiFactory(
		article: Article                      # defines createArticle()
		db: PDO(%dsn%, %user%, %password%)    # defines getDb()
	)

あるいは、ファクトリーの定義で、参照を使用して既存のサービスを参照できます:

services:
	article: Article
	- PDO(%dsn%, %user%, %password%)
	- MultiFactory(
		article: @article    # defines createArticle()
		db: @\PDO            # defines getDb()
	)

タグ付き定義

マルチファクトリーを定義するもう一つの方法は、タグを使うことです。

services:
	- App\Core\RouterFactory::createRouter
	- App\Model\DatabaseAccessor(
		db1: @database.db1.explorer
	)