こんにちは、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()についての解説はしませんでしたが、また別の機会に解説をしたいと思います。