イントロダクション

サービスプロバイダーはLaravelアプリケーション全体における起動処理初期の心臓部です。皆さんのアプリケーションと同じく、Laravelのコアサービス全部もサービスプロバイダーを利用して初期起動処理を行っています。

ところで「初期起動処理」とは何を意味しているのでしょうか? サービスコンテナの結合やイベントリスナー、フィルター、それにルートなどを登録することを一般的に意味しています。サービスプロバイダーはアプリケーション設定の中心部です。

Laravelに含まれているconfig/app.phpファイルを開けば、providers配列が見つかるでしょう。そこにある全サービスプロバイダークラスが、アプリケーションのためにロードされます。もちろんほとんどのプロバイダーは、全てのリクエストで必ずロードされるとは限らず、そのプロバイダーが提供するサービスが必要なときにのみロードされる「遅延」プロバイダーです。

この概論ではサービスプロバイダーの書き方と、Laravelアプリケーションに登録する方法を学びます。

サービスプロバイダーの記述

全てのサービスプロバイダーは、Illuminate\Support\ServiceProviderクラスを拡張します。この抽象クラスは皆さんのプロバイダーの中に、最低でもregisterメソッドだけは定義することを求めています。registerメソッドの中ではサービスコンテナへの登録だけを行わなくてはなりません。他のイベントリスナーやルート、その他の機能の一部でも、registerメソッドの中で登録しようとしてはいけません。

make:provider Artisanコマンドラインにより、簡単に新しいプロバイダーが生成できます。

php artisan make:provider RiakServiceProvider

Registerメソッド

既に説明した通り、registerメソッドの中ではサービスコンテナに何かを結合することだけを行わなければなりません。イベントリスナーやルート、その他のどんな作業もregisterメソッドの中では決して行ってはいけません。これを守らないと、サービスプロバイダーがまだロードしていないサービスを意図せず使ってしまう羽目になるでしょう。

では、基本的なプロバイダーを見てみましょう。

<?php

namespace App\Providers;

use Riak\Connection;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * コンテナへの結合登録
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('Riak\Contracts\Connection', function ($app) {
            return new Connection(config('riak'));
        });
    }
}

このサービスプロバイダーではregisterメソッドだけが定義されています。そして、サービスコンテナにRiak\Contracts\Connectionの実装を定義しています。サービスコンテナがどのように動作するのかまだ理解できていなければ、ドキュメントを調べてください。

Bootメソッド

ではイベントリスナーをサービスプロバイダーで登録する必要がある場合は、どうすればよいのでしょうか? bootメソッドの中で行ってください。このメソッドは、他の全サービスプロバイダーが登録し終えてから呼び出されます。つまりフレームワークにより登録された、他のサービス全てにアクセスできるのです。

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
    /**
     * サービス起動の登録後に、実行される
     *
     * @return void
     */
    public function boot()
    {
        view()->composer('view', function () {
            //
        });
    }

    /**
     * コンテナへの結合登録
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

bootメソッドの依存注入

bootメソッドでは依存をタイプヒントにより指定可能です。サービスコンテナは必要な依存を自動的に注入してくれます。

use Illuminate\Contracts\Routing\ResponseFactory;

public function boot(ResponseFactory $factory)
{
    $factory->macro('caps', function ($value) {
        //
    });
}

プロバイダーの登録

全てのサービスプロバイダーは、config/app.php設定ファイルで登録されています。このファイルには起動するサービスプロバイダーの名前をリストしてあるproviders配列が含まれています。この配列にはデフォルトとして、コアのサービスプロバイダーが登録されています。これらのプロバイダーは、メール送信、キュー、キャッシュなどのLaravelコアコンポーネントの初期起動を行っています。

プロバイダーを登録するには、この配列に追加するだけです。

'providers' => [
    // その他のサービスプロバイダー

    App\Providers\AppServiceProvider::class,
],

遅延プロバイダー

もし皆さんのプロバイダーが、サービスコンテナへコンテナ結合を登録するだけであるなら、その結合が実際に必要になるまで登録を遅らせる方が良いでしょう。こうしたプロバイダーのローディングを遅らせるのは、リクエストがあるたびにファイルシステムからロードされなくなるため、アプリケーションのパフォーマンスを向上させます。

プロバイダーを遅延ロードするには、deferプロパティーにtrueをセットし、providesメソッドを定義します。providesメソッドはそのプロバイダーで登録するサービスコンテナ結合名を返します。

<?php

namespace App\Providers;

use Riak\Connection;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * プロバイダーのローディングを遅延させるフラグ
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * サービスプロバーダーの登録
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('Riak\Contracts\Connection', function ($app) {
            return new Connection($app['config']['riak']);
        });
    }

    /**
     * このプロバイダーにより提供されるサービス
     *
     * @return array
     */
    public function provides()
    {
        return ['Riak\Contracts\Connection'];
    }

}

Laravelは遅延サービスプロバイダーが提示した全サービスのリストをコンパイルし、サービスプロバイダーのクラス名と共に保存します。その後、登録されているサービスのどれか一つを依存解決する必要が起きた時のみ、Laravelはそのサービスプロバイダーをロードします。