【Laravel】簡単にファサードを自作する方法を解説

Laravel

こんにちは、aiiro(@aiiro29)です。

前回はファサードの実装を読んで、ファサードとはどういうものなのかを説明しました。

今回はファサードを自作して使用する方法を説明します。

スポンサー

要約

  • Illuminate\Support\Facades\Facadeを継承したファサードクラスを作成し、getFacadeAccessor()を実装する
  • connfig/app.phpでファサードを登録する

説明

今回作成する独自ファサードの例として、Chronosを使うことにします。

ChronosはCarbonと同じように日付を扱うライブラリで、CakePHPで使用されていますが、CakePHP以外でも使用することができるようになっています。

Carbonとの大きな違いは、Carbonのオブジェクトがミュータブルであるのに対し、Chronosのオブジェクトがイミュータブルであることです。

イミュータブルなオブジェクトであるため、バグが起こり難いと言われています。

実際Carbonをイミュータブルにしたいという人達も多くいて、GitHubの公式リポジトリでも以前からIssueが発行されていますが、これまでのところイミュータブルにはなっていません。

そういった議論はありますが、LaravelでChronosを使えるようにするのも面白そうなので、Chronosを導入して、ファサードを使用できるようにしたいと思います。

Chronosをインストールする

最初はcomposerを使ってChronosをインストールします。


composer require cakephp/chronos

ファサードを作成する

Chronosのインストールができたら、次はファサードを作成していきます。

自作ファサードクラスを格納するためのディレクトリをapp/Facadesとして作成しましょう。

ディレクトリが用意できたら、ディレクトリ配下にapp/Facades/Chronos.phpを作成します。


namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Chronos extends Facade
{

    /**
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return \Cake\Chronos\Chronos::class;
    }
}

ファサードを実装する際は、Illuminate\Support\Facades\Facadeを継承し、getFacadeAccessor()を実装します。

ファサードを登録する

ファサードクラスの作成が済んだら、connfig/app.phpにファサードのエイリアスを登録します。


'aliases' => [

    'App' => Illuminate\Support\Facades\App::class,
    // 中略
    'View' => Illuminate\Support\Facades\View::class,

    // Custom Facades
    'Chronos' => App\Facades\Chronos::class,
],

以上でファサードの作成は完了です。

以後は設定したエイリアス名でファサードを呼び出せるようになります。

自作ファサードの使い方


$today = \Chronos::today();
var_dump($today);

class Cake\Chronos\Chronos#655 (3) {
  public $time =>
  string(26) "2018-07-31 00:00:00.000000"
  public $timezone =>
  string(3) "UTC"
  public $hasFixedNow =>
  bool(false)
}

独自ファサードは簡単に追加することができますので、公開されているライブラリを導入する際などにファサードを作成してみると良いかと思います。

一歩踏み込んで

独自ファサードの作成方法については説明した通りです。

ここからは、Laravelが追加された独自ファサードをどのようにして解決しているかを見ていきます。

前回の記事で、ファサードはIlluminate\Support\Facades\FacadeクラスのresolveFacadeInstance()を使用してインスタンスを取得していることを説明しました。


/**
 * Resolve the facade root instance from the container.
 *
 * @param  string|object  $name
 * @return mixed
 */
protected static function resolveFacadeInstance($name)
{
    if (is_object($name)) {
        return $name;
    }

    if (isset(static::$resolvedInstance[$name])) {
        return static::$resolvedInstance[$name];
    }

    return static::$resolvedInstance[$name] = static::$app[$name];
}

追加した独自ファサードも同じようにこのメソッドを通ることになります。

そして、static::$app[$name]の結果が返却されるのですが、static::$appはIlluminate\Foundation\Applicationクラスのインスタンスです。

ApplicationクラスはIlluminate\Container\Containerクラスを継承しており、ArrayAccessをimplementsしています。

そのため、$app[$name]のように配列として参照された場合、Containerクラスで定義されている、offsetGet()が実行されることになります。


/**
 * Get the value at a given offset.
 *
 * @param  string  $key
 * @return mixed
 */
public function offsetGet($key)
{
    return $this->make($key);
}

/**
 * Resolve the given type from the container.
 *
 * @param  string  $abstract
 * @param  array  $parameters
 * @return mixed
 */
public function make($abstract, array $parameters = [])
{
    return $this->resolve($abstract, $parameters);
}

更にoffsetGet()はmake()を経由してresolve()を呼び出します。

そしてこのresolve()でインスタンスを解決し、返却しています。

以上が独自ファサードがコンテナで解決されるまでの処理の流れです。

今回はresolve()についての解説はしませんでしたが、また別の機会に解説をしたいと思います。