イントロダクションIntroduction
Laravelのサービスコンテナは、クラス間の依存を管理する強力な管理ツールです。依存注入というおかしな言葉は主に「コンストラクターか、ある場合にはセッターメソッドを利用し、あるクラスをそれらに依存しているクラスへ外部から注入する」という意味で使われます。The Laravel service container is a powerful tool for managing class dependencies. Dependency injection is a fancy word that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
シンプルな例を見てみましょう。Let's look at a simple example:
<?php namespace App\Handlers\Commands;
use App\User;
use App\Commands\PurchasePodcast;
use Illuminate\Contracts\Mail\Mailer;
class PurchasePodcastHandler {
/**
* メイラーの実装
*/
protected $mailer;
/**
* 新しいインスタンスの生成
*
* @param Mailer $mailer
* @return void
*/
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
/**
* ポッドキャストの購入
*
* @param PurchasePodcastCommand $command
* @return void
*/
public function handle(PurchasePodcastCommand $command)
{
//
}
}
この例でPurchasePodcast
コマンドハンドラーは、ポッドキャスト購入時にメールを送信する必要があります。そのため、メールを送信できるサービスを注入しています。サービスが外部から注入されているため、簡単に他の実装と交換できます。さらに「モック」したり、アプリケーションのテストを行うためにメーラーのダミー実装を作成したりするのも簡単に実現できます。In this example, the PurchasePodcast
command handler needs to send e-mails when a podcast is purchased. So, we will inject a service that is able to send e-mails. Since the service is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the mailer when testing our application.
Laravelのサービスコンテナを深く理解することは、パワフルで大きなアプリケーションを構築することと同時に、Laravelコア自身に貢献するために重要です。A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself.
基本的な使用法Basic Usage
結合Binding
ほぼ全てのサービスコンテナへの登録は、サービスプロバイダーの中で行われるでしょう。そのため以降のサンプルコードは、コンテナをプロバイダー内で使用するデモンストレーションになっています。しかし、例えばファクトリーのような、アプリケーションのどこか他の場所で、コンテナのインスタンスが必要になったら、Illuminate\Contracts\Container\Container
契約をタイプヒントで指定すれば、コンテナのインスタンスが注入されるでしょう。もしくは、App
ファサードでコンテナにアクセスすることもできます。Almost all of your service container bindings will be registered within service providers[/docs/master/providers], so all of these examples will demonstrate using the container in that context. However, if you need an instance of the container elsewhere in your application, such as a factory, you may type-hint the Illuminate\Contracts\Container\Container
contract and an instance of the container will be injected for you. Alternatively, you may use the App
facade to access the container.
基本的なリゾルバーの登録Registering A Basic Resolver
サービスプロバイダーの中では、コンテナへアクセスするには$this->app
インスタンス変数を使用する必要があります。Within a service provider, you always have access to the container via the $this->app
instance variable.
サービスコンテナへ依存を登録するには、多くの方法があります。この中には、クロージャーのコールバックやインターフェイスと実装の結合も含まれます。最初に、クロージャーのコールバックを説明しましょう。クロージャーのリゾルバーは、キー(典型的な使用法ではクラス名)と値を返すクロージャーをコンテナの中で登録します。There are several ways the service container can register dependencies, including Closure callbacks and binding interfaces to implementations. First, we'll explore Closure callbacks. A Closure resolver is registered in the container with a key (typically the class name) and a Closure that returns some value:
$this->app->bind('FooBar', function($app)
{
return new FooBar($app['SomethingElse']);
});
シングルトンの登録Registering A Singleton
場合により、コンテナに結合した何かを一度だけ依存解決したいことがあると思います。コンテナが何度呼び出されても、同じインスタンスが返されます。Sometimes, you may wish to bind something into the container that should only be resolved once, and the same instance should be returned on subsequent calls into the container:
$this->app->singleton('FooBar', function($app)
{
return new FooBar($app['SomethingElse']);
});
存在するインスタンスをコンテナへ結合Binding An Existing Instance Into The Container
既に存在するオブジェクトのインスタンスをinstance
メソッドを用いて、コンテナに結合することできます。指定されたインスタンスが、以降のコンテナで呼び出されるたびに返されます。You may also bind an existing object instance into the container using the instance
method. The given instance will always be returned on subsequent calls into the container:
$fooBar = new FooBar(new SomethingElse);
$this->app->instance('FooBar', $fooBar);
依存解決Resolving
コンテナから何かの依存を解決して取り出すには、様々な方法が取れます。最初に、make
メソッドを使用してみましょう。There are several ways to resolve something out of the container. First, you may use the make
method:
$fooBar = $this->app->make('FooBar');
コンテナはPHPのArrayAccess
を実装していますので、次にコンテナへの「配列アクセス」を使ってみましょう。Secondly, you may use "array access" on the container, since it implements PHP's ArrayAccess
interface:
$fooBar = $this->app['FooBar'];
最後に一番重要な、コンテナにより依存解決される、コントローラーやイベントリスナー、キュージョブ、フィルターなどのクラスのコンストラクターで、依存を「タイプヒント」するだけの方法です。コンテナは自動的に、依存を注入します。Lastly, but most importantly, you may simply "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers, event listeners, queue jobs, filters, and more. The container will automatically inject the dependencies:
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Users\Repository as UserRepository;
class UserController extends Controller {
/**
* ユーザーリポジトリーインスタンス
*/
protected $users;
/**
* 新しいコントローラーインスタンスの生成
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* 指定されたIDのユーザーを表示する
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}
インターフェイスと実装の結合Binding Interfaces To Implementations
具象依存クラスの注入Injecting Concrete Dependencies
サービスコンテナーのとても強力な機能は、インターフェイスを指定された実装に結合することです。例えば、アプリケーションが、リアルタイムイベントを送受信するサービスであるPusher Webサービスと統合されているとしましょう。PusherのPHP DSKを使用していれば、クラスへPusherクライアントのインスタンスを注入することができます。A very powerful features of the service container is its ability to bind an interface to a given implementation. For example, perhaps our application integrates with the Pusher[https://pusher.com] web service for sending and receiving real-time events. If we are using Pusher's PHP SDK, we could inject an instance of the Pusher client into a class:
<?php namespace App\Handlers\Commands;
use App\Commands\CreateOrder;
use Pusher\Client as PusherClient;
class CreateOrderHandler {
/**
* Pusher SDKクライアントインスタンス
*/
protected $pusher;
/**
* 新しい注文処理インスタンスの生成
*
* @param PusherClient $pusher
* @return void
*/
public function __construct(PusherClient $pusher)
{
$this->pusher = $pusher;
}
/**
* 指定されたコマンドの実行
*
* @param CreateOrder $command
* @return void
*/
public function execute(CreateOrder $command)
{
//
}
}
この例の良い点は、クラス依存を外部から注入していることです。ですが、Pusher SDKときつく結びついています。Pusher SDKに変更が合ったり、新しいイベントサービスに全体を移行する決定をしたりすると、CreateOrderHandler
コードを変更しなくてはなりません。In this example, it is good that we are injecting the class dependencies; however, we are tightly coupled to the Pusher SDK. If the Pusher SDK methods change or we decide to switch to a new event service entirely, we will need to change our CreateOrderHandler
code.
インターフェイスに対するプログラムProgram To An Interface
イベント送信の変更に対して、CreateOrderHandler
を「分離」させるため、EventPusher
インターフェイスと、PusherEventPusher
実装を定義することができます。In order to "insulate" the CreateOrderHandler
against changes to event pushing, we could define an EventPusher
interface and a PusherEventPusher
implementation:
<?php namespace App\Contracts;
interface EventPusher {
/**
* 新しいイベントを全クライアントにPushする
*
* @param string $event
* @param array $data
* @return void
*/
public function push($event, array $data);
}
このインターフェイスに対して、PusherEventPusher
実装をコードし終えたら、次のようにサービスコンテナで登録します。Once we have coded our PusherEventPusher
implementation of this interface, we can register it with the service container like so:
$this->app->bind('App\Contracts\EventPusher', 'App\Services\PusherEventPusher');
これはコンテナへ、EventPusher
の実装が必要になったら、PusherEventPusher
を注入するように指示しています。This tells the container that it should inject the PusherEventPusher
when a class needs an implementation of EventPusher
. Now we can type-hint the EventPusher
interface in our constructor:
/**
* 新しい注文処理インスタンスの生成
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}
コンテキストに合わせた結合Contextual Binding
時々、同じインターフェイスを使用した2つのクラスがあり、クラスごとに異なった実装を注入する必要がある場合もあるでしょう。例えば、システムが新しい注文(order)を受けた時は、Pusherの代わりに、PubNubを利用してイベントを送る必要がある場合です。Laravelは、このような振る舞いを定義できる、シンプルで読みやすいインターフェイスを提供しています。Sometimes you may have two classes that utilize the same interface, but you wish to inject different implementations into each class. For example, when our system receives a new Order, we may want to send an event via PubNub[http://www.pubnub.com/] rather than Pusher. Laravel provides a simple, fluent interface for definining this behavior:
$this->app->when('App\Handlers\Commands\CreateOrderHandler')
->needs('App\Contracts\EventPusher')
->give('App\Services\PubNubEventPusher');
タグ付けTagging
ある場合には、ある明確な「カテゴリー」の結合を全部解決するする必要があると思います。例えば、Report
インターフェイスの実装である、異なった多くの配列を受け取る、レポート収集プログラム(aggregator)を構築しているとしましょう。それらをtag
メソッでタグ付けすることができます。Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report aggregator that receives an array of many different Report
interface implementations. After registering the Report
implementations, you can assign them a tag using the tag
method:
$this->app->bind('SpeedReport', function()
{
//
});
$this->app->bind('MemoryReport', function()
{
//
});
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
サービスにタグを付けてしまえば、tagged
メソッドで簡単に全部解決できます。Once the services have been tagged, you may easily resolve them all via the tagged
method:
$this->app->bind('ReportAggregator', function($app)
{
return new ReportAggregator($app->tagged('reports'));
});
アプリケーションでの実践Practical Applications
Laravelではアプリケーションの柔軟性とテスタビリティーを上げるため、サービスコンテナを利用する多くの機会があります。一つの重要な例は、コントローラーの依存解決時です。コントローラーは全てサービスコンテナを通じて解決されます。つまり、コントローラーのコンストラクターの中で、依存をタイプヒントで指定でき、それらは自動的に注入されます。Laravel provides several opportunities to use the service container to increase the flexibility and testability of your application. One primary example is when resolving controllers. All controllers are resolved through the service container, meaning you can type-hint dependencies in a controller constructor, and they will automatically be injected.
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* 注文リポジトリーインスタンス
*/
protected $orders;
/**
* コントローラーインスタンスの生成
*
* @param OrderRepository $orders
* @return void
*/
public function __construct(OrderRepository $orders)
{
$this->orders = $orders;
}
/**
* 全注文の表示
*
* @return Response
*/
public function index()
{
$all = $this->orders->all();
return view('orders', ['all' => $all]);
}
}
この例では、OrderRepository
クラスは自動的にコントローラーへ注入されます。つまりユニットテストを行う場合は、コンテナに「モック」のOrderRepository
が結合され、データベース層のやりとりを苦労なくスタブ化させてくれるのです。In this example, the OrderRepository
class will automatically be injected into the controller. This means that a "mock" OrderRepository
may be bound into the container when unit testing[/docs/master/testing], allowing for painless stubbing of database layer interaction.
他のコンテナ使用例Other Examples Of Container Usage
もちろん、前記の通り、コントローラーだけがLaravelで、サービスコンテナにより依存解決されるクラスではありません。ルートクロージャーやフィルター、キュージョブ、イベントリスナーなどでも依存をタイプヒントで指定できます。そうしたコンテキストでのコンテナ使用例は、各ドキュメントを参照してください。Of course, as mentioned above, controllers are not the only classes Laravel resolves via the service container. You may also type-hint dependencies on route Closures, filters, queue jobs, event listeners, and more. For examples of using the service container in these contexts, please refer to their documentation.
コンテナイベントContainer Events
リゾルバーリスナーの登録Registering A Resolving Listener
コンテナーは、オブジェクトを依存解決した時に毎回イベントを発行します。このイベントは、resolving
を使用して、リッスンできます。The container fires an event each time it resolves an object. You may listen to this event using the resolving
method:
$this->app->resolving(function($object, $app)
{
// どんなタイプのものでもコンテナが依存解決した時に呼び出される…
});
$this->app->resolving(function(FooBar $fooBar, $app)
{
// "FooBar"タイプのオブジェクトがコンテナにより依存解決された時に呼び出される…
});
依存解決されたオブジェクトが、コールバックに渡されます。The object being resolved will be passed to the callback.