イントロダクション
サービスプロバイダは、Laravelアプリケーション全体の起動処理における、初めの心臓部です。皆さんのアプリケーションと同じく、Laravelのコアサービス全部もサービスプロバイダを利用し、初期起動処理を行っています。
ところで「初期起動処理」とは何を意味しているのでしょうか? サービスコンテナの結合や、イベントリスナ、フィルター、それにルートなどを登録することを一般的に意味しています。サービスプロバイダはアプリケーション設定の中心部です。
Laravelに含まれているconfig/app.php
ファイルを開くと、providers
配列を見つけるでしょう。これらはすべて、アプリケーションにロードされるサービスプロバイダクラスです。デフォルトでは、Laravelコアサービスプロバイダのセットがこの配列にリストされています。これらのプロバイダは、メーラー、キュー、キャッシュなどのコアLaravelコンポーネントを初期起動処理します。これらのプロバイダの多くは「遅延」プロバイダです。つまり、すべてのリクエストで読み込まれるわけではなく、提供するサービスが実際に必要な場合にのみ読み込まれます。
この概論ではサービスプロバイダの書き方と、Laravelアプリケーションに登録する方法を学びます。
Note: Laravelがリクエストをどのように処理し、内部で動作しているかについて詳しく知りたい場合は、Laravelのリクエストライフサイクルに関するドキュメントを確認してください。
サービスプロバイダの記述
すべてのサービスプロバイダは、Illuminate\Support\ServiceProvider
クラスを拡張します。ほとんどのサービスプロバイダは、register
とboot
メソッドを持っています。register
メソッドの中ではサービスコンテナへの登録だけを行わなくてはなりません。他のイベントリスナやルート、その他の機能の一部でも、register
メソッドの中で登録しようとしてはいけません。
make:provider
Artisanコマンドラインにより、新しいプロバイダが生成できます。
php artisan make:provider RiakServiceProvider
Registerメソッド
すでに説明した通り、register
メソッドの中ではサービスコンテナに何かを結合することだけを行わなければなりません。イベントリスナやルート、その他のどんな機能もregister
メソッドの中では決して行ってはいけません。これを守らないと、サービスプロバイダがまだロードしていないサービスを意図せず使ってしまう羽目になるでしょう。
では、基本的なサービスプロバイダを見てみましょう。サービスプロバイダメソッド中であれば、いつでも$app
プロパティを利用でき、サービスコンテナへアクセスできます。
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider
{
/**
* 全アプリケーションサービスの登録
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
このサービスプロバイダはregister
メソッドのみを定義し、このメソッドを使用してサービスコンテナ内のApp\Services\Riak\Connection
の実装を定義します。Laravelのサービスコンテナにまだ慣れていない方は、ドキュメントで確認してください。
bindings
とsingletons
プロパティ
サービスプロバイダでシンプルな結合をたくさん登録しているのであれば、各コンテナ結合を自力で登録する代わりに、bindings
とsingletons
プロパティを使いたくなるでしょう。フレームワークにより、サービスプロバイダがロードされる時点で、これらのプロパティがチェックされ、結合を登録します。
<?php
namespace App\Providers;
use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 登録する必要のある全コンテナ結合
*
* @var array
*/
public $bindings = [
ServerProvider::class => DigitalOceanServerProvider::class,
];
/**
* 登録する必要のある全コンテナシングルトン
*
* @var array
*/
public $singletons = [
DowntimeNotifier::class => PingdomDowntimeNotifier::class,
ServerProvider::class => ServerToolsProvider::class,
];
}
Bootメソッド
では、ビューコンポーサをサービスプロバイダで登録する必要がある場合は、どうすればよいのでしょうか? boot
メソッドの中で行ってください。このメソッドは、他の全サービスプロバイダが登録し終えてから呼び出されます。つまりフレームワークにより登録された、他のサービスすべてにアクセスできるのです。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
/**
* 全アプリケーションサービスの初期起動処理
*
* @return void
*/
public function boot()
{
View::composer('view', function () {
//
});
}
}
bootメソッドの依存注入
サービスプロバイダのboot
メソッドでは、依存をタイプヒントで指定できます。サービスコンテナが、必要な依存を自動的に注入します。
use Illuminate\Contracts\Routing\ResponseFactory;
/**
* アプリケーションの全サービスの初期起動処理
*
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
* @return void
*/
public function boot(ResponseFactory $response)
{
$response->macro('serialized', function ($value) {
//
});
}
プロバイダの登録
すべてのサービスプロバイダは、config/app.php
設定ファイルで登録されています。このファイルには、サービスプロバイダの名前をリストしてあるproviders
配列が含まれています。この配列にはデフォルトとして、メール送信、キュー、キャッシュなどのLaravelコアのサービスプロバイダが登録されています。
プロバイダを登録するには、この配列に追加します。
'providers' => [
// Other Service Providers
App\Providers\ComposerServiceProvider::class,
],
遅延プロバイダ
もし皆さんのプロバイダが、サービスコンテナへコンテナ結合を登録するだけであるなら、その結合が実際に必要になるまで登録を遅らせる方が良いでしょう。こうしたプロバイダのローディングを遅らせるのは、リクエストがあるたびにファイルシステムからロードされなくなるため、アプリケーションのパフォーマンスを向上させます。
Laravelは遅延サービスプロバイダが提示した全サービスのリストをコンパイルし、サービスプロバイダのクラス名と共に保存します。その後、登録されているサービスのどれか一つを依存解決する必要が起きた時のみ、Laravelはそのサービスプロバイダをロードします。
プロバイダを遅延ロードするには、\Illuminate\Contracts\Support\DeferrableProvider
インターフェイスを実装し、provides
メソッドを定義します。provides
メソッドはそのプロバイダで登録したサービスコンテナ結合を返します。
<?php
namespace App\Providers;
use App\Services\Riak\Connection;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* 全アプリケーションサービスの登録
*
* @return void
*/
public function register()
{
$this->app->singleton(Connection::class, function ($app) {
return new Connection($app['config']['riak']);
});
}
/**
* このプロバイダにより提供されるサービスの取得
*
* @return array
*/
public function provides()
{
return [Connection::class];
}
}