イントロダクション

ファサードはアプリケーションのサービスコンテナに用意したクラスに「静的」なインターフェイスを提供しています。Laravelは多くのファサードを使用していますが、多分皆さんは気が付かないまま使用していることでしょう! Laravelの「ファサード(facade)」は、サービスコンテナ下で動作しているクラスに対し、"static proxy"として動作しています。これにより伝統的な静的メソッドよりも簡潔で、テストの行いやすさと柔軟性を保ちながらも、記述的な書き方が行えます。

自分のアプリケーションやパッケージでも、ファサードを作成したい場合があるでしょう。そのため、コンセプトと開発方法、クラスの使い方を説明していきます。

注目: ファサードを学び始める前に、Laravelのサービスコンテナに慣れておくことを強くおすすめします。

解説

Laravelアプリケーションに関する文脈で「ファサード」は、コンテナを通じてオブジェクトにアクセス方法を提供するクラスのことを意味します。Facadeクラス中のメカニズムでこれを行なっています。Laravelのファサードと、皆さんが作成するカスタムファサードは、Facadeクラスを拡張します。

ファサードクラスで実装する必要があるのはgetFacadeAccessorだけです。getFacadeAccessorメソッドの役目はコンテナを通じたインスタンスの依存解決に何を使用するかを定義することです。Facade基本クラスは__callStatic()マジックメソッドを使用し、あなたのファサードからの呼び出しを依存解決してインスタンス化したオブジェクトへと届けます。

Cache::getのようにファサードの呼び出しが行われると、LaravelはサービスコンテナでCacheマネージャーを依存解決し、そのクラスのgetメソッドを呼び出します。技術的な言い方をすれば、Laravelのファサードは、サービスローケーターとしてのLaravelのサービスコンテナを使用した、使いやすい記法のことです。

実際の使用

下の例では、Laravelのキャッシュシステムを呼び出しています。これを読むと、一見Cacheクラスのstaticなgetメソッドが呼び出されていのだと考えてしまうことでしょう。

$value = Cache::get('key');

ところが、Illuminate\Support\Facades\Cacheクラスを見てもらえば、staticのgetメソッドは存在していないことが分かります。

class Cache extends Facade {

    /**
     * コンポーネントの登録名を取得する
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'cache'; }

}

CacheファサードはベースのFacadeクラスを拡張し、getFacadeAccessor()メソッドを定義しています。思い出してください。このメソッドの仕事はサービスコンテナで結合した名前を返すことでした。

ユーザーがCacheファサードのどのstaticメソッドを利用しようと、Laravelはサービスコンテナからcacheに結び付けられたインスタンスを依存解決し、要求されたメソッドを(この場合はget)そのオブジェクトに対し実行します。

ですから、Cache::getの呼び出しは以下のように書き直すこともできます。

$value = $app->make('cache')->get('key');

ファサードのインポート

コントローラーが名前空間下にあるときにファサードを利用するには、その名前空間へファサードクラスをインポートする必要があることを覚えておいてください。全てのファサードはグローバル名前空間にあります。

<?php namespace App\Http\Controllers;

use Cache;

class PhotosController extends Controller {

    /**
     * 写真を全て取得する
     *
     * @return Response
     */
    public function index()
    {
        $photos = Cache::get('photos');

        //
    }

}

ファサードの作成

ファサードを作成するとアプリケーションやパッケージをシンプルにすることができます。必要なのは3つだけです。

  • サービスコンテナへの結合
  • ファサードクラス
  • ファサードエイリアスの設定

例を見てください。PaymentGateway\Paymentクラスを定義しています。

namespace PaymentGateway;

class Payment {

    public function process()
    {
        //
    }

}

このクラスをサービスコンテナで依存解決できるようにする必要があります。そのため、サービスプロバイダーに結合を追加しましょう。

App::bind('payment', function()
{
    return new \PaymentGateway\Payment;
});

この結合を設置する良い方法は、PaymentServiceProviderという名前の新しいサービスプロバイダーを作成し、registerメソッドへ追加することでしょう。次にconfig/app.php設定ファイルで、このサービスプロバイダーがロードされるように指定しましょう。

次に、ファサードクラスを作成しましょう。

use Illuminate\Support\Facades\Facade;

class Payment extends Facade {

    protected static function getFacadeAccessor() { return 'payment'; }

}

最後にお望みであれば、config/app.php設定ファイルのaliases配列に、このファサードのエイリアスを付け加えることもできます。これで、Paymentクラスインスタンスのprocessメソッドを呼び出すことができるようになりました。

Payment::process();

オートローディングエイリアスの注意点

PHPが未定義のタイプヒントをオートロードしないため、aliases配列にインスタンスが指定できない場合があります。もし、\ServiceWrapper\ApiTimeoutExceptionApiTimeoutExceptionというエイリアス名で定義し、\ServiceWrapper外の名前空間でcatch(ApiTimeoutException $e)しても、投げられたその例外は捕捉されません。似たような問題は、エイリアスされたクラスへのタイプヒントを持つモデルでも、見かけられます。唯一の依存解決法は、そのようなエイリアスの参照に先立ち、それぞれのファイルの先頭で、必要なタイプヒントをuseで指定しておく方法しかありません。

ファサードのモック

ファサードがどうしてこのような仕組みを持っているのか、その理由の重要な一面がユニットテストです。実際、ファサードが存在している一番大きな理由がテストの行いやすさです。詳細はドキュメントのファサードのモックをご覧ください。

ファサードクラス一覧

以下は全ファサードと、実際のクラスの一覧です。これは特定のファサードを元にし、APIドキュメントを素早く探したい場合に便利な道具になります。対応するサービスコンテナ結合キーも記載しています。

ファサード クラス サービスコンテナ結合
App Illuminate\Foundation\Application app
Artisan Illuminate\Console\Application artisan
Auth Illuminate\Auth\AuthManager auth
Auth (インスタンス) Illuminate\Auth\Guard
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\CacheManager cache
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (インスタンス) Illuminate\Database\Connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Hash Illuminate\Contracts\Hashing\Hasher hash
Input Illuminate\Http\Request request
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Password Illuminate\Auth\Passwords\PasswordBroker auth.password
Queue Illuminate\Queue\QueueManager queue
Queue (インスタンス) Illuminate\Queue\QueueInterface
Queue (ベースクラス) Illuminate\Queue\Queue
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint
Session Illuminate\Session\SessionManager session
Session (インスタンス) Illuminate\Session\Store
Storage Illuminate\Contracts\Filesystem\Factory filesystem
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (インスタンス) Illuminate\Validation\Validator
View Illuminate\View\Factory view
View (インスタンス) Illuminate\View\View