Laravelのファサードとはなにか

ずっと使っているけど十分に理解できなかったので調べた。

ファサードとはなにか

結論から言うと、ファサードは、対応したサービスのインスタンスのメソッドを、静的にコールできる仕組み、のようだ。

GoFデザインパターンのFacadeパターンとはちょっと違う。あれは複数の複雑な機能に対する一つのアクセス方法を提供するものだったと思うので。こっちはもっとシンプル。多少の例外はあるが、基本的にはサービスに委譲するだけだ。

Illuminate\Support\Facades\Requestファサード(以下Requestファサード)を例に挙げる。このファサード'request'エイリアスのサービスと対応していて、その実体はIlluminate\Http\Requestインスタンスである。

コントローラアクションで、Illuminate\Http\RequestをDIして持って来た$requestのたとえば$request->all()と、ファサードRequest::all()は同等になる。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request as HttpRequest;
use Illuminate\Support\Facades\Request;

class TestController extends Controller
{
    public function index(HttpRequest $request)
    {
        dd($request->all(), Request::all()); # 等しい
    }
}

ファサードはどう実装されているか

まずファサードはクラスである。Illuminate\Support\Facades名前空間以下にあるクラスで、Illuminate\Support\Facades\Facade抽象クラスを継承している。

ファサードクラス内にはgetFacadeAccessor()というメソッドが共通して存在していて、これが肝になっている。単純にこの戻り値によって対応するサービスが決まっている。

大抵文字列で、サービスのエイリアスが返されているが、Schemaファサードに関してはオブジェクトが直接返されていたりする。

オブジェクトの場合はそのまま使うだけだが、サービスのエイリアス等の場合は、必要に応じてresolve()相当の解決が行われている。

つまりRequestファサードで静的にメソッドを呼び出すのと、resolve('request')されたオブジェクトのメソッドを呼び出すのも同じことになる。

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Request;

class TestController extends Controller
{
    public function index()
    {
        dd(resolve('request')->all(), Request::all()); # 等しい
    }
}

関連1: クラス名のエイリアス

ファサードに関連する紛らわしい個所が2つある。1つはクラス名のエイリアスで、ファサードとは直接関係ないがほぼファサードに関連して使われている。

これはuse Requestuse Illuminate\Support\Facades\Requestと同等になるあたりの話だ。

通常エイリアスを使うことはほぼ使わないと思うが、一部の素のPHPスクリプト上では、useせず、エイリアスが直接使われていることがあるかもしれない。

php artisan ui vue --authなどとして認証機能を有効にした場合、routes/web.phpAuth::routes()が追加されるが、このときファイル上部ではuse Illuminate\Support\Facades\Authとはされていないはずだ。

これはエイリアスのおかげで動作している。

エイリアスは単純に、spl_autoload_register()を使って、動的にclass_alias()されることで設定されている。

設定されているエイリアスに関しては、Illuminate\Foundation\AliasLoader::getInstance()->getAliases()をtinker等から実行すれば確認できる。

これらのほとんどはconfig/app.phpaliasesで設定されるものだが、一部Composerでインストールされたパッケージから設定されるものがある。composer.lock(じっさいにはinstalled.jsonから読まれているが)のパッケージ設定にextra.laravel.aliasesがある場合、config/app.phpaliasesのものに追加でエイリアスが作られる。

デフォルトではLaravel 7のエラー表示パッケージであるIgnitionから、Flareクラスのエイリアスが設定されているはずだ。

// ...
        {
            "name": "facade/ignition",
            // ...
            "extra": {
                "laravel": {
                    "aliases": {
                        "Flare": "Facade\\Ignition\\Facades\\Flare"
                    }
                }
            },
        },
// ...

関連2: Arr, Str

もう1つはIlluminate\Support以下の、Arr, Strクラスだ。これらのクラスはファサードではないが、静的メソッドの使用がメインで、かつエイリアスも設定されているため、場合によってはファサードと区別がしづらいかもしれない。

とはいえ区別する必要もあまりない。クラスのソースコードを見れば、一目で裏にサービスインスタンスのない静的メソッドだけのクラスとわかる。