はじめに
Laravelを使っていると
「ファサード」という言葉がちょこちょこ出てきたりします。
ファサードとな何なのか、
何のためにあるのか、
どういう仕組みで動いているのか、
など調べてみたので簡単に解説していきます。
※
本記事内ではサービスコンテナを利用したコードが多数登場しますが
サービスコンテナ自体の仕組みは解説しません。
サービスコンテナについてはこちらの記事で解説しています。
【Laravel】サービスコンテナとは?2つの強力な武器を持ったインスタンス化マシーン。簡単に解説。
概要
ファサードとは、
クラスをインスタンス化しなくてもstaticメソッドのように
メソッドを実行できるようにしてくれる機能のこと。
こんな風にできる。
// 通常
$classA = new ClassA();
$classA->methodA();
// ファサード利用
FacadeClassA::methodA();
このインスタンス化している1行の手間をなくせるっていうだけっちゃだけですかね。
ファサードの作り方
自分でファサードを作る方法です。
元クラス作成
class ClassA
{
public function methodA()
{
return 'methodA called!!';
}
}
ただmethodA()
というメソッドを持っているだけの
ClassAです。
先ほどの概要説明の例で見た通り、
このmethod()
を実行したかったら
まずClassA
をインスタンス化してからメソッドを呼ぶ必要がありますね。
// 通常
$classA = new ClassA();
$classA->methodA();
それを、このような形で呼び出せるようにしていきます。
// ファサード利用
FacadeClassA::methodA();
サービスコンテナに登録
ファサードとして利用したいクラスは、
サービスコンテナに登録します。1
public function register() {
app()->bind('classA', ClassA::class);
}
このコードは、ファサードを利用する前ならどこに書いても問題ないですが、
通常はサービスプロバイダに書きますね。
ファサードクラス作成
次に、ファサードクラスを作ります。
use Illuminate\Support\Facades\Facade;
class FacadeClassA extends Facade
{
protected static function getFacadeAccessor()
{
return 'classA';
}
}
ポイントは3つだけです。
-
Illuminate\Support\Facades\Facade
をextendsすること -
getFacadeAccessor()
メソッドを定義すること - サービスコンテナに登録してあるキーワードをreturnすること
呼び出す
これで自作ファサードは完成です。
最初の例のように呼び出すことができます。
use App\Facades\FacadeClassA; // useする
FacadeClassA::methodA();
// methodA called!!
これで、ClassAをインスタンス化しなくても
FacadeClassAを利用することでmethodA()
を実行できました。
エイリアス登録
ここまでの説明で、ファサードは利用できるようになりましたが、
通常はさらにひと手間加えたりします。
/config/app.php
のaliases
配列に
自作ファサードの設定を追記します。
'aliases' => [
'FacadeClassA' => \App\Facades\FacadeClassA::class,
]
このエイリアス登録を行うことで、
先ほどの呼び出し方法から
use App\Facades\FacadeClassA;
このuseが不要になります。
そして、
FacadeClassA::methodA();
↓
\FacadeClassA::methodA();
この様に頭に「\」を1つつけるだけで
呼び出すことができるようになります。
インスタンス化の手間を削減
+
長ったらしい名前空間を記述する手間削減
っていうことですね。
ファサードの仕組み
\FacadeClassA::methodA();
この様なコードで、
どうやってClassA
のmethodA()
が実行されているのか、
ファサードの仕組みを見てみます。
FacadeClassA
のmethodA()
がstaticメソッドとして呼ばれている状態ですが、
FacadeClassAの中にはmethodA()
は存在しません。
class FacadeClassA extends Facade
{
protected static function getFacadeAccessor()
{
return 'classA';
}
}
その親クラスである
Illuminate\Support\Facades\Facade
を見てみます。
すると、一番下にこのメソッドがあります。
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
return $instance->$method(...$args);
}
この__callStatic()
は、
PHP自体が持っているマジックメソッドで
「クラスに存在しないメソッドをstaticに呼び出したときに実行される」
というメソッドです。
($method
には呼び出そうとしたメソッド名が入っています)
つまり、
FacadeClassA
に存在しないmethodA()
をstaticに呼び出そうとしたので、
この__callStatic()
が実行されることになります。
$instance = static::getFacadeRoot();
の中を見てみます。
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
ここで、FacadeClassA
に自分で実装したgetFacadeAccessor()
が実行されています。
getFacadeAccessor()
は文字列classA
を返すので、
この様な形になります。
public static function getFacadeRoot()
{
return static::resolveFacadeInstance('classA');
}
さらにこのresolveFacadeInstance()
の中を見てみます。
protected static function resolveFacadeInstance($name)
{
return static::$app[$name];
}
$name
には先ほどの文字列classA
は入っています。
$app
には、サービスコンテナが入っているので、
この1行では
return app()->make('classA');
と同じ意味と思って大丈夫です。
サービスコンテナを利用してClassAをインスタンス化しています。
最初の__callStatic()
に戻ってみましょう。
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
return $instance->$method(...$args);
}
今見た通り、getFacadeRoot()
の先では
ClassAをインスタンス化して返却していましたので、
$instance
にはClassAのインスタンスが入っています。
$method
にはmethodA
という文字列が入っているので、
return $instance->$method(...$args);
この一行でClassAのmethodA()
を実行しているのが分かります。
\FacadeClassA::methodA();
この様にファサードとして実行されると、
内部では
__callStatic()
が呼び出され、
getFacadeAccessor()
で定義したキーワードでClassAをインスタンス化し、
そのClassAのmethodA()
を実行していた
ということが分かりました。
既存ファサード
今回はファサードの仕組みを学ぶために
ファサードクラスを自作しましたが、
Laravelがもともと用意してくれている便利なファサードクラスがたくさんあります。
Laravel 6.x ファサード:ファサードクラス一覧
\Auth
や\DB
などファサードとして利用したことがある人も多いと思います。
おわりに
Laravelドキュメントによると、
ファサードのメリットはこのように書かれています。
1つ目。
ファサードにはたくさんの利点があります。自分で取り込んだり、設定したりする必要があり、長くて覚えにくいクラス名を使わずに、Laravelの機能を簡素で覚えやすい文法で使ってもらえます。
これは、本記事でも言及しました。
いちいちインスタンス化しなくてもいいという点と、
エイリアス登録することで長いクラス名を省略できる
っていうところですね。
2つ目。
その上に、PHPの動的メソッドのユニークな使用方法のおかげで、簡単にテストができます。
このテストでの利点については
僕自身がまだ実際に経験していないので
今後チェックしていきたいところです。
そして、デメリットについても言及されています。
しかしながら、ファサードの使用にはいくつか気をつけるべき点も存在します。ファサードの一番の危険性は、クラスの責任範囲の暴走です。
ファサードが簡単で便利すぎるから、
ついつい1つのクラス内でいろんなファサード使いまくりがち。
そうするとクラスの責任範囲が大きくなりすぎて危険だよ。
っていう話ですね。
今回解説したファサードの仕組みと、
上記のメリットデメリットをしっかり把握したうえで
ファサードを活用していきたいです。
Laravelのちょっと深いところを理解していきたいと思っている方、
これらの記事もおすすめですので是非読んでみてください。
(次に進む前に、LGTMしてもらえるとうれしいです)
魔法のようなLaravelも素のPHPに始まり素のPHPに終わる。Laravelのライフサイクルを簡単に解説。
【Laravel】サービスコンテナとは?2つの強力な武器を持ったインスタンス化マシーン。簡単に解説。
【Laravel】引数でタイプヒントしただけでインスタンスがもらえるのはなぜ?Laravelの魔法を解明してみる。
【Laravel】ファサードとは?何が便利か?どういう仕組みか?
-
サービスコンテナに登録しなくても
getFacadeAccessor()
でそのままクラス名を返せば利用可能ですが、通常はサービスコンテナに登録することが多い
↩