#経緯
プログラマとして業務を始めてから間もないタイミングで新人研修の担当を任され、改めて自分が上辺の知識で業務を行っていたのか日々痛感しています・・・。
Laravelのファサードもその一つで、いざ教えるとなると「そういえばファサードってなんだ・・・?」となってしまったので言語化できるように調べてまとめました。
#公式から
ファサード(facade、「入り口」)はアプリケーションのサービスコンテナに登録したクラスへ、「静的」なインターフェイスを提供します。Laravelのほとんどの機能に対して、ファサードが用意されています。Laravelの「ファサード」は、サービスコンテナ下で動作しているクラスに対し、"static proxy"として動作しています。これにより伝統的な静的メソッドよりもテストの行いやすさと柔軟性を保ちながらも、簡潔で記述的であるという利点があります。
Laravelのファサードはすべて、Illuminate\Support\Facades名前空間下で定義されています。ですから、簡単にファサードへアクセスできます
https://readouble.com/laravel/7.x/ja/facades.html
クラスをLaravelサービスコンテナ内に記載する事で、簡単に独自クラスのメソッドを呼び出せるようにした機能。
またある程度の内容であれば、デフォルトでLaravelに用意されているファサードが利用できる。
#語源から
Facadeパターン(wiki)
(建物の)正面、上辺、外観を意味する単語であるファサード(Facade)は複雑なプログラムでお互いに関係しあっているたくさんのクラスを簡単に制御できるように、決められた処理を行うために用意された窓口です。
使う側は複雑なたくさんのクラスを個別に制御する必要がなく、窓口(正面)だけを見ればよいという事でこの名前が付けられており、Laravelで利用されているFacadeとデザインパターンであるFacadeパターンでは厳密には同じではないのですが、Facadeパターンを基にして、Facadeがつくられています。
#具体的にわかりやすく端的に
わかりやすい記事があったのでそこから抜粋
// 通常
$classA = new ClassA();
$classA->methodA();
// ファサード利用
FacadeClassA::methodA();
FacadeClassA::の部分がファサードです。ファサードを利用すると
毎回クラスを new するのに比べて、記述量も少なく、メモリー効率、パフォーマンス効率が上がります。
#根元をたどっていく
Route::get('/cache', function () {
return Cache::get('key');
});
公式で載せられていたCacheファサードを追っていきましょう。
###config\app.php
Cacheファサードで使用されているCacheクラスはconfig\app.phpのaliasesに記載されています。
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
:
なるほどCacheクラスはIlluminateの下にあるのか・・・。
あれ?Illuminateディレクトリってどこにあるんだろう。パスがIlluminateから始まってるのにプロジェクト直下にない・・・。
となりましたが、Illuminateディレクトリはvender\laravel\framework\src\
にありました。
###何故Illuminateから始まっているのにvender\laravel\framework\src\の物が読まれるのか
Laravelはクラスの自動ローディング規約であるPSR-4ローディング規約に従っています。
名前空間を含めたクラス名と、ファイルパスを対応付ける仕組みで、従来の指定方法ではディレクトリー構造が深くなってしまうため、改良したのがPSR-4規約になっています。
確かにこれをそのまま書くのは長すぎますね・・・。
ちなみにLaravelが指定しているローディング規約はcomposer.jsonの中で指定しています。
:
"autoload": {
"files": [
"src/Illuminate/Foundation/helpers.php",
"src/Illuminate/Support/helpers.php"
],
"psr-4": {
"Illuminate\\": "src/Illuminate/"
}
}
:
ありました。
"psr-4"アイテムでIlluminate\"キーに対してsrc/Illuminate/を指定していますね。
これでIlluminateに対してsrc/Illuminate/ディレクトリを呼び出していることがわかりました。
ちなみにvenderディレクトリはComposerの依存パッケージが配置されているディレクトリです。
###Illuminate\Support\Facades\Cache.php
呼び出しの仕組みがわかった所でクラスファイルの中身に進みます。
<?php
namespace Illuminate\Support\Facades;
/**
* @see \Illuminate\Cache\CacheManager
* @see \Illuminate\Cache\Repository
*/
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}
ファイルの中身を見るとCacheクラスがFacadeクラスを継承している事と、return 'cache'
する関数があることがわかりました。
次は継承元のFacadeクラスを見てみましょう。
###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);
}
Facade.phpファイルの一番下には__callStaticメソッドがあります。
この__callStaticメソッドはマジックメソッドの一種で、アクセス不能なメソッドを静的に実行しようとしたタイミングで呼び出されます。
注)マジックメソッドとは特定の条件下において実行されるメソッドです。
今回cacheクラスにもFacadeクラスにもCache::get()メソッドで呼び出したget()メソッドはありませんでしたね。
なので、マジックメソッドである__callStaticメソッドが実行されるわけです。
__callStaticメソッドの中を見てみると
getFacadeRoot()
が呼び出され、getFacadeRoot()でcacheクラスのgetFacadeAccessor()
が呼び出されている事がわかります。
cacheクラスのgetFacadeAccessor()では'cache'
を返していましたね。
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
今回は'cache'
を引数にresolveFacadeInstanceが呼び出されているわけですが、それぞれのファサードのgetFacadeAccessor()の戻り値が引数に入る事がわかりました。
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];
}
ここからresolvedInstanceで最初に説明したようなインスタンスを作成して、実行という流れを実行しているわけです。
$classA = new ClassA();
$classA->methodA();
#おわりに
ファサードの理解にあたって、まだまだ必要な知識が多く(サービスコンテナやサービスプロバイダー等)、
かなり部分的な追及になってしまっていますが、もし私のような立場になって質問された時、ここまで説明出来たら及第点なのかなと思います。
ただLaravelの理解には必須だと思いますので、お時間のある時に調べてみる事をおすすめします。