イントロダクション
Laravelのコントロールの逆転(Inversion of control)コンテナはクラスの依存性を管理するパワフルなツールです。依存性の注入は、ハードコードされたクラスの依存を取り除く手法です。代わりに依存は実行時に注入され、依存的な実装を簡単に交換できる、素晴らしい柔軟性をもたらせてくれます。
Laravelのコアで実際に役立っているIoCコンテナを理解することは、パワフルで大きなアプリケーションを構築する際に重要です。
基本的な使い方
タイプをコンテナに結びつける
IoCコンテナが依存を解決する方法は2つあります。クロージャーのコールバックによる方法と自動的な解決です。最初はクロージャーのコールバックを説明しましょう。初めにコンテナに「タイプ」を結びつけます。
App::bind('foo', function($app)
{
return new FooBar;
});
コンテナでタイプを解決する
$value = App::make('foo');
App::make
メソッドが呼び出されると、クロージャーコールバックが実行され、結果がリターンされます。
「共用」のタイプをコンテナと結びつける
場合により、一度だけ解決される何かをコンテナに結び付けたいこともあるでしょう。毎回コンテナを呼び出す代わりに、同じインスタンスを返してくれるほうが望ましいこともあります。
App::singleton('foo', function()
{
return new FooBar;
});
存在するインスタンスをコンテナに結びつける
また、既に存在するインスタンスをコンテナと結合したいこともあるでしょう。instance
メソッドを使用します。
$foo = new Foo;
App::instance('foo', $foo);
どこで結合を登録するか
イベントハンドラーやルートフィルターのようなIoC結合は、一般的には「初期化コード」と呼ばれる一部に当たります。言い換えれば、これらはアプリケーションで実際にリクエストを処理する準備を行い、通常ルートやコントローラーが実際に呼び出される前に実行される必要があります。他の多くの準備コードと同様に、いつもstart
ファイルはIoCの結合を登録する選択肢の一つでしょう。もしくは、(ファイル名は何でも良いのですが)app/ioc.php
ファイルを作成し、start
ファイルからrequireで取り込むこともできます。
アプリケーションでとても多くのIoC結合が必要だったり、カテゴリーによりIoC結合を別々のファイルに組織化したい場合は、サービスプロバイダーで結合を登録して下さい。
自動的な解決
クラスを解決する
IoCコンテナは多くの状況で、設定を全く行なわずとも、とても強力にクラスを解決します。例えば:
class FooBar {
public function __construct(Baz $baz)
{
$this->baz = $baz;
}
}
$fooBar = App::make('FooBar');
コンテナでFooBarクラスを登録しなくても、クラスを解決できることに注目してください。コンテナはBaz
に対してさえ依存を自動的に注入し解決します。
あるタイプがコンテナと結びついていない場合、クラスを調べ、コンストラクターのタイプヒントを読み取るために、PHPのReflection機能が使われます。これにより取得した情報により、コンテナは自動的にクラスのインスタンスを生成します。
インターフェイスを実装と結びつける
しかしながら、「具象タイプ」ではなく、インターフェイスの実装に依存しているケースもあります。そうした場合、どのインターフェイスを実装したクラスが注入されるのかをApp::bind
メソッドで知らせる必要があります。
App::bind('UserRepositoryInterface', 'DbUserRepository');
では、以下のようなコントローラーを考えてみましょう。
class UserController extends BaseController {
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
}
UserRepositoryInterface
を実際のタイプと結びつけてあるため、このコントローラーが生成された時、DbUserRepository
が自動的に注入されます。
実践的な使い方
LaravelはIoCコンテナを使用し、アプリケーションの柔軟性とテストをしやすくする多くの機能を提供しています。良い例がコントローラーの解決です。全てのコントローラーはIoCコンテナを通じて解決されます。これが意味しているのは、コントローラーのコンストラクターにおけるタイプヒントの依存により、自動的に注されるということです。
タイプヒントのコントローラー依存
class OrderController extends BaseController {
public function __construct(OrderRepository $orders)
{
$this->orders = $orders;
}
public function getIndex()
{
$all = $this->orders->all();
return View::make('orders', compact('all'));
}
}
この例でOrderRepository
クラスはコントローラーに自動的に注入されます。これが意味するのは、ユニットテストを行う際に、「モック」としてのOrderRepository
がコンテナーにより結び付けられ、コントローラーに注入され、データベースレイヤーの関係を面倒なしに代用(スタブ)してくれると言うことです。
IoCが使用される別の例
フィルター、コンポーサー、イベントハンドラーもIoCコンテナにより解決されます。登録する時、シンプルに使用されるクラスの名前を指定してください。
Route::filter('foo', 'FooFilter');
View::composer('foo', 'FooComposer');
Event::listen('foo', 'FooHandler');
サービスプロバイダー
サービスプロパイダーは関係するIoC登録を一つの場所にまとめる素晴らしい方法です。アプリケーションのブートストラップコンポーネントとして捉えてください。サービスプロバイダーの中で、カスタム認証ドライバーを登録したり、アプリケーションのリポジトリクラスをIoCコンテナに登録したり、カスタムArtisanコマンドを準備したりさえできます。
実際、Laravelのコアコンポーネントにサービスプロパイダーが含まれています。アプリケーションの全ての登録されたサービースプロバイダーはapp/config/app.php
設定ファイルの中で、providers
としてリストされています。
サービスプロバイダを定義する
サービスプロバイダを生成するには、ただIlluminate\Support\ServiceProvider
クラスを拡張し、register
メソッドを定義するだけです。
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bind('foo', function()
{
return new Foo;
});
}
}
register
メソッドの中で、アプリケーションIoCコンテナは$this->app
プロパティを通じ利用できることに注目してください。一度プロバイダーを作成すれば、アプリケーションに登録する準備はでき、後はただapp
設定ファイルのproviders
配列に付け加えるだけです。
実行時にサービスプロバイダーを登録する
また、実行時にApp::register
メソッドによりサービスプロバイダーを登録することもできます。
App::register('FooServiceProvider');
コンテナイベント
Resolvingリスナーを登録
コンテナはオブジェクトの解決時に毎回イベントを発行します。このイベントは`resolving'メソッドを使用しリッスンできます。
App::resolvingAny(function($object)
{
//
});
App::resolving('foo', function($foo)
{
//
});
解決されたオブジェクトがコールバックに渡されることに注目してください。