生成されたファクトリ
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
ファクトリには、Article
オブジェクトを作成する
create
メソッドがあります。Article
クラスは、例えば次のようになります:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
) {
}
}
ファクトリを設定ファイルに追加します:
services:
- ArticleFactory
Nette DIは対応するファクトリの実装を生成します。
ファクトリを使用するコード内で、インターフェースに基づいてオブジェクトを要求し、Nette DIは生成された実装を使用します:
class UserController
{
public function __construct(
private ArticleFactory $articleFactory,
) {
}
public function foo()
{
// ファクトリにオブジェクトを作成させる
$article = $this->articleFactory->create();
}
}
パラメータ化されたファクトリ
ファクトリメソッド create
はパラメータを受け取ることができ、その後それらをコンストラクタに渡します。例えば、Article
クラスに記事の著者IDを追加しましょう:
class Article
{
public function __construct(
private Nette\Database\Connection $db,
private int $authorId,
) {
}
}
パラメータをファクトリにも追加します:
interface ArticleFactory
{
function create(int $authorId): Article;
}
コンストラクタのパラメータとファクトリのパラメータが同じ名前であるという理由で、Nette 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()
が呼び出されたときに初めてデータベースオブジェクトが作成されます:
アクセサを作成するには?インターフェースを書くだけで、Nette
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
に対して異なる戻り値を区別することはできません。
リストによる定義
この方法で、設定内で複数ファクトリを定義できます:
services:
- MultiFactory(
article: Article # createArticle() を定義します
db: PDO(%dsn%, %user%, %password%) # getDb() を定義します
)
または、ファクトリの定義内で、参照を使用して既存のサービスを参照できます:
services:
article: Article
- PDO(%dsn%, %user%, %password%)
- MultiFactory(
article: @article # createArticle() を定義します
db: @\PDO # getDb() を定義します
)
タグによる定義
2番目の選択肢は、定義にタグを利用することです:
services:
- App\Core\RouterFactory::createRouter
- App\Model\DatabaseAccessor(
db1: @database.db1.explorer
)