Laravel ファサードってなんだ?

四角ズボン
2024-04-25
2024-04-25

Laravelを使っていると「ファサード(Facade)」という記述が要所要所で出てきます。
Route, Cache, Log...などいろいろあってとても便利な機能なのですが、使うばっかりで仕組みや内部の動きについてよく理解できていなかったので、調べてみました。

公式サイトには以下の通り記述があります。
(Laravel10.XドキュメントをGoogle翻訳で日本語化)

ファサードは、アプリケーションのサービス・コンテナで使用可能なクラスへの「静的」インタフェースを提供します。Laravelには、Laravelのほぼすべての機能にアクセスできる多くのファサードが付属しています。

Laravelが用意している便利機能を、インスタンス化しなくても静的メソッドとして実行できるようにしてくれる、ということのようです。

使い方は、Illuminate\Support\Facades 名前空間で定義し、
アクセスするには次のように静的メソッドのように呼び出すだけです。

use Illuminate\Support\Facades\Log;

Log::debug('hello')

では、ファサードの中はどうなっているのか、
Illuminate\Support\Facades\Logクラスを見てみると、これだけです。

class Log extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'log';
    }
}

静的メソッド debug() はありません。

では、この debug() メソッド はいったいどこにあってどのように実行されているのでしょうか??

順を追うと、以下のような処理になっています。

24.03.08_Laravel_Facade

  1. Logクラスには debug() メソッドが存在しないため、マジックメソッドの __classStatic() メソッドが呼び出される
  2. getFacadeRoot()が呼び出され、以下のように処理される
    1. getFacadeAccessor()が呼び出される。このメソッドは、Logによってオーバーライドされており、サービスプロバイダに設定された名前(この場合は 'log')が返却される
    2.  resolveFacadeInstance()が呼び出され、static::$app['log']が返される。static::$appはサービスコンテナから log サービスのインスタンスを起動させる app()->make('log') と同じことを意味する
    3. LogServiceProvider からサービスのインスタンス(この場合は 'LogManager')を取得する
  3. 取得したサービスのインスタンスに対して、引数で渡ってきたメソッド名(この場合は 'debug')と引数(この場合は 'Hello')を呼び出し、結果をそのまま返す

Facade::__callStatic()
[Illuminate\Support\Facades\Facade.php]

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return $instance->$method(...$args);
}

Log::getFacadeAccessor()
[Illuminate\Support\Facades\Log.php]

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

LogServiceProvider
[Illuminate\Log\LogServiceProvider.php]

class LogServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('log', function ($app) {
            return new LogManager($app);
        });
    }
}

 LogManager::debug()
[Illuminate\Log\LogManager.php]

public function debug($message, array $context = []): void
{
    $this->driver()->debug($message, $context);
}

ファサードの実行の流れは以上です。

ファサードはとても便利な機能ですが、
  • 1つのクラスで多く使いすぎると依存関係が増えてクラスが大きくなりすぎる
  • テストの実行が難しくなる可能性がある

といったデメリットもあるようです。

上手に活用していきたいです。