Nette Documentation Preview

syntax
Nette DIの拡張機能の作成
****************

.[perex]
DIコンテナの生成は、設定ファイルに加えて、いわゆる*拡張機能*によっても影響を受けます。これらは、設定ファイルの `extensions` セクションで有効化します。

このようにして、`BlogExtension` クラスによって表現される拡張機能を `blog` という名前で追加します:

```neon
extensions:
	blog: BlogExtension
```

各コンパイラ拡張機能は [api:Nette\DI\CompilerExtension] を継承し、DIコンテナのビルド中に順次呼び出される以下のメソッドを実装できます:

1. getConfigSchema()
2. loadConfiguration()
3. beforeCompile()
4. afterCompile()


getConfigSchema() .[method]
===========================

このメソッドが最初に呼び出されます。設定パラメータの検証のためのスキーマを定義します。

拡張機能は、拡張機能が追加された名前と同じ名前のセクション、つまり `blog` で設定します:

```neon
# extension と同じ名前
blog:
	postsPerPage: 10
	allowComments: false
```

すべての設定オプション、それらの型、許可された値、そして場合によってはデフォルト値を含むスキーマを作成します:

```php
use Nette\Schema\Expect;

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function getConfigSchema(): Nette\Schema\Schema
	{
		return Expect::structure([
			'postsPerPage' => Expect::int(),
			'allowComments' => Expect::bool()->default(true),
		]);
	}
}
```

ドキュメントは [スキーマ |schema:] ページにあります。さらに、`dynamic()` を使用してどのオプションが[動的 |application:bootstrapping#動的パラメータ]であるかを指定できます。例:`Expect::int()->dynamic()`。

設定には、`stdClass` オブジェクトである `$this->config` 変数を通じてアクセスします:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$num = $this->config->postPerPage;
		if ($this->config->allowComments) {
			// ...
		}
	}
}
```


loadConfiguration() .[method]
=============================

コンテナにサービスを追加するために使用されます。これには [api:Nette\DI\ContainerBuilder] を使用します:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();
		$builder->addDefinition($this->prefix('articles'))
			->setFactory(App\Model\HomepageArticles::class, ['@connection']) // または setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}
```

慣習として、拡張機能によって追加されたサービスには、名前の衝突が発生しないように、その名前でプレフィックスを付けます。これは `prefix()` メソッドが行うため、拡張機能の名前が `blog` であれば、サービスは `blog.articles` という名前を持ちます。

サービスの名前を変更する必要がある場合、後方互換性を維持するために、元の名前でエイリアスを作成できます。Netteは、例えば `routing.router` サービスで同様のことを行っています。これは以前の名前 `router` でも利用可能です。

```php
$builder->addAlias('router', 'routing.router');
```


ファイルからのサービスのロード
---------------

サービスは、ContainerBuilderクラスのAPIを使用して作成するだけでなく、設定ファイルNEONのservicesセクションで使用されるよく知られた記法でも作成できます。プレフィックス `@extension` は現在の拡張機能を表します。

```neon
services:
	articles:
		create: MyBlog\ArticlesModel(@connection)

	comments:
		create: MyBlog\CommentsModel(@connection, @extension.articles)

	articlesList:
		create: MyBlog\Components\ArticlesList(@extension.articles)
```

サービスをロードします:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();

		// 拡張機能の設定ファイルをロード
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}
```


beforeCompile() .[method]
=========================

このメソッドは、コンテナが `loadConfiguration` メソッドで個々の拡張機能によって追加されたすべてのサービス、およびユーザー設定ファイルによって追加されたすべてのサービスを含む時点で呼び出されます。したがって、ビルドのこの段階で、サービス定義を修正したり、それらの間の関連を補完したりできます。コンテナ内のサービスをタグで検索するには `findByTag()` メソッドを、クラスまたはインターフェースで検索するには `findByType()` メソッドを利用できます。

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function beforeCompile()
	{
		$builder = $this->getContainerBuilder();

		foreach ($builder->findByTag('logaware') as $serviceName => $tagValue) {
			$builder->getDefinition($serviceName)->addSetup('setLogger');
		}
	}
}
```


afterCompile() .[method]
========================

この段階では、コンテナクラスはすでに [ClassType |php-generator:#クラス] オブジェクトの形式で生成されており、サービスを作成するすべてのメソッドを含み、キャッシュへの書き込み準備ができています。この時点で、結果のクラスコードをさらに修正できます。

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function afterCompile(Nette\PhpGenerator\ClassType $class)
	{
		$method = $class->getMethod('__construct');
		// ...
	}
}
```


$initialization .[method]
=========================

Configuratorクラスは、[コンテナ作成後 |application:bootstrapping#index.php] に初期化コードを呼び出します。これは、[メソッド addBody() |php-generator:#メソッドと関数の本体] を使用して `$this->initialization` オブジェクトに書き込むことによって作成されます。

例えば、初期化コードでセッションを開始したり、`run` タグを持つサービスを開始したりする方法の例を示します:

```php
class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// セッションの自動開始
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// run タグを持つサービスはコンテナのインスタンス化後に作成される必要がある
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}
```

Nette DIの拡張機能の作成

DIコンテナの生成は、設定ファイルに加えて、いわゆる拡張機能によっても影響を受けます。これらは、設定ファイルの extensions セクションで有効化します。

このようにして、BlogExtension クラスによって表現される拡張機能を blog という名前で追加します:

extensions:
	blog: BlogExtension

各コンパイラ拡張機能は Nette\DI\CompilerExtension を継承し、DIコンテナのビルド中に順次呼び出される以下のメソッドを実装できます:

  1. getConfigSchema()
  2. loadConfiguration()
  3. beforeCompile()
  4. afterCompile()

getConfigSchema()

このメソッドが最初に呼び出されます。設定パラメータの検証のためのスキーマを定義します。

拡張機能は、拡張機能が追加された名前と同じ名前のセクション、つまり blog で設定します:

# extension と同じ名前
blog:
	postsPerPage: 10
	allowComments: false

すべての設定オプション、それらの型、許可された値、そして場合によってはデフォルト値を含むスキーマを作成します:

use Nette\Schema\Expect;

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function getConfigSchema(): Nette\Schema\Schema
	{
		return Expect::structure([
			'postsPerPage' => Expect::int(),
			'allowComments' => Expect::bool()->default(true),
		]);
	}
}

ドキュメントは スキーマ ページにあります。さらに、dynamic() を使用してどのオプションが動的であるかを指定できます。例:Expect::int()->dynamic()

設定には、stdClass オブジェクトである $this->config 変数を通じてアクセスします:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$num = $this->config->postPerPage;
		if ($this->config->allowComments) {
			// ...
		}
	}
}

loadConfiguration()

コンテナにサービスを追加するために使用されます。これには Nette\DI\ContainerBuilder を使用します:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();
		$builder->addDefinition($this->prefix('articles'))
			->setFactory(App\Model\HomepageArticles::class, ['@connection']) // または setCreator()
			->addSetup('setLogger', ['@logger']);
	}
}

慣習として、拡張機能によって追加されたサービスには、名前の衝突が発生しないように、その名前でプレフィックスを付けます。これは prefix() メソッドが行うため、拡張機能の名前が blog であれば、サービスは blog.articles という名前を持ちます。

サービスの名前を変更する必要がある場合、後方互換性を維持するために、元の名前でエイリアスを作成できます。Netteは、例えば routing.router サービスで同様のことを行っています。これは以前の名前 router でも利用可能です。

$builder->addAlias('router', 'routing.router');

ファイルからのサービスのロード

サービスは、ContainerBuilderクラスのAPIを使用して作成するだけでなく、設定ファイルNEONのservicesセクションで使用されるよく知られた記法でも作成できます。プレフィックス @extension は現在の拡張機能を表します。

services:
	articles:
		create: MyBlog\ArticlesModel(@connection)

	comments:
		create: MyBlog\CommentsModel(@connection, @extension.articles)

	articlesList:
		create: MyBlog\Components\ArticlesList(@extension.articles)

サービスをロードします:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		$builder = $this->getContainerBuilder();

		// 拡張機能の設定ファイルをロード
		$this->compiler->loadDefinitionsFromConfig(
			$this->loadFromFile(__DIR__ . '/blog.neon')['services'],
		);
	}
}

beforeCompile()

このメソッドは、コンテナが loadConfiguration メソッドで個々の拡張機能によって追加されたすべてのサービス、およびユーザー設定ファイルによって追加されたすべてのサービスを含む時点で呼び出されます。したがって、ビルドのこの段階で、サービス定義を修正したり、それらの間の関連を補完したりできます。コンテナ内のサービスをタグで検索するには findByTag() メソッドを、クラスまたはインターフェースで検索するには findByType() メソッドを利用できます。

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function beforeCompile()
	{
		$builder = $this->getContainerBuilder();

		foreach ($builder->findByTag('logaware') as $serviceName => $tagValue) {
			$builder->getDefinition($serviceName)->addSetup('setLogger');
		}
	}
}

afterCompile()

この段階では、コンテナクラスはすでに ClassType オブジェクトの形式で生成されており、サービスを作成するすべてのメソッドを含み、キャッシュへの書き込み準備ができています。この時点で、結果のクラスコードをさらに修正できます。

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function afterCompile(Nette\PhpGenerator\ClassType $class)
	{
		$method = $class->getMethod('__construct');
		// ...
	}
}

$initialization

Configuratorクラスは、コンテナ作成後 に初期化コードを呼び出します。これは、メソッド addBody() を使用して $this->initialization オブジェクトに書き込むことによって作成されます。

例えば、初期化コードでセッションを開始したり、run タグを持つサービスを開始したりする方法の例を示します:

class BlogExtension extends Nette\DI\CompilerExtension
{
	public function loadConfiguration()
	{
		// セッションの自動開始
		if ($this->config->session->autoStart) {
			$this->initialization->addBody('$this->getService("session")->start()');
		}

		// run タグを持つサービスはコンテナのインスタンス化後に作成される必要がある
		$builder = $this->getContainerBuilder();
		foreach ($builder->findByTag('run') as $name => $foo) {
			$this->initialization->addBody('$this->getService(?);', [$name]);
		}
	}
}