【Laravel】ファサードとは？何が便利か？どういう仕組みか？

# はじめに
Laravelを使っていると
「ファサード」という言葉がちょこちょこ出てきたりします。

ファサードとな何なのか、
何のためにあるのか、
どういう仕組みで動いているのか、
など調べてみたので簡単に解説していきます。

※
本記事内ではサービスコンテナを利用したコードが多数登場しますが
サービスコンテナ自体の仕組みは解説しません。
サービスコンテナについてはこちらの記事で解説しています。
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)


# 概要
ファサードとは、
クラスをインスタンス化しなくてもstaticメソッドのように
メソッドを実行できるようにしてくれる機能のこと。

こんな風にできる。

```php
// 通常
$classA = new ClassA();
$classA->methodA();

// ファサード利用
FacadeClassA::methodA();
```

このインスタンス化している１行の手間をなくせるっていうだけっちゃだけですかね。


# ファサードの作り方
自分でファサードを作る方法です。

## 元クラス作成

```php:ClassA
class ClassA
{
    public function methodA()
    {
        return 'methodA called!!';
    }
}
```

ただ`methodA()`というメソッドを持っているだけの
ClassAです。

先ほどの概要説明の例で見た通り、
この`method()`を実行したかったら
まず`ClassA`をインスタンス化してからメソッドを呼ぶ必要がありますね。

```php
// 通常
$classA = new ClassA();
$classA->methodA();
```

それを、このような形で呼び出せるようにしていきます。

```php
// ファサード利用
FacadeClassA::methodA();
```

## サービスコンテナに登録
ファサードとして利用したいクラスは、
サービスコンテナに登録します。[^1]
[^1]: サービスコンテナに登録しなくても`getFacadeAccessor()`でそのままクラス名を返せば利用可能ですが、通常はサービスコンテナに登録することが多い
　

```php:/app/Providers/AppServiceProvider.php
public function register() {
    app()->bind('classA', ClassA::class);
}
```

このコードは、ファサードを利用する前ならどこに書いても問題ないですが、
通常はサービスプロバイダに書きますね。


## ファサードクラス作成
次に、ファサードクラスを作ります。

```php:FacadeClassA.php
use Illuminate\Support\Facades\Facade;

class FacadeClassA extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'classA';
    }
}
```

ポイントは３つだけです。

* `Illuminate\Support\Facades\Facade`をextendsすること
* `getFacadeAccessor()`メソッドを定義すること
* サービスコンテナに登録してあるキーワードをreturnすること

## 呼び出す
これで自作ファサードは完成です。
最初の例のように呼び出すことができます。

```php
use App\Facades\FacadeClassA; // useする

FacadeClassA::methodA();
// methodA called!!
```

これで、ClassAをインスタンス化しなくても
FacadeClassAを利用することで`methodA()`を実行できました。

## エイリアス登録
ここまでの説明で、ファサードは利用できるようになりましたが、
通常はさらにひと手間加えたりします。

`/config/app.php`の`aliases`配列に
自作ファサードの設定を追記します。

```php:/config/app.php
'aliases' => [
    'FacadeClassA' => \App\Facades\FacadeClassA::class,
]
```

このエイリアス登録を行うことで、
先ほどの呼び出し方法から
`use App\Facades\FacadeClassA;`このuseが不要になります。

そして、
`FacadeClassA::methodA();`
↓
`\FacadeClassA::methodA();`
この様に頭に「\」を１つつけるだけで
呼び出すことができるようになります。

インスタンス化の手間を削減
＋
長ったらしい名前空間を記述する手間削減
っていうことですね。


# ファサードの仕組み

```php
\FacadeClassA::methodA();
```

この様なコードで、
どうやって`ClassA`の`methodA()`が実行されているのか、
ファサードの仕組みを見てみます。

`FacadeClassA`の`methodA()`がstaticメソッドとして呼ばれている状態ですが、
FacadeClassAの中には`methodA()`は存在しません。

```php:FacadeClassA.php
class FacadeClassA extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'classA';
    }
}
```

その親クラスである
`Illuminate\Support\Facades\Facade`を見てみます。

すると、一番下にこのメソッドがあります。

```php:Facade.php（重要箇所のみ抜粋）
public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

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

この`__callStatic()`は、
PHP自体が持っているマジックメソッドで
「クラスに存在しないメソッドをstaticに呼び出したときに実行される」
というメソッドです。
（`$method`には呼び出そうとしたメソッド名が入っています）

つまり、
`FacadeClassA`に存在しない`methodA()`をstaticに呼び出そうとしたので、
この`__callStatic()`が実行されることになります。


`$instance = static::getFacadeRoot();`の中を見てみます。

```php:Facade.php
public static function getFacadeRoot()
{
    return static::resolveFacadeInstance(static::getFacadeAccessor());
}
```

ここで、`FacadeClassA`に自分で実装した`getFacadeAccessor()`が実行されています。
`getFacadeAccessor()`は文字列`classA`を返すので、
この様な形になります。

```php:Facade.php
public static function getFacadeRoot()
{
    return static::resolveFacadeInstance('classA');
}
```

　
さらにこの`resolveFacadeInstance()`の中を見てみます。

```php:Facade.php（重要箇所のみ抜粋）
protected static function resolveFacadeInstance($name)
{
    return static::$app[$name];
}
```

`$name`には先ほどの文字列`classA`は入っています。
`$app`には、サービスコンテナが入っているので、
この１行では
`return app()->make('classA');`と同じ意味と思って大丈夫です。

サービスコンテナを利用してClassAをインスタンス化しています。


最初の`__callStatic()`に戻ってみましょう。

```php:Facade.php（重要箇所のみ抜粋）
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()`を実行しているのが分かります。


```php
\FacadeClassA::methodA();
```

この様にファサードとして実行されると、
内部では
`__callStatic()`が呼び出され、
`getFacadeAccessor()`で定義したキーワードでClassAをインスタンス化し、
そのClassAの`methodA()`を実行していた
ということが分かりました。


# 既存ファサード
今回はファサードの仕組みを学ぶために
ファサードクラスを自作しましたが、
Laravelがもともと用意してくれている便利なファサードクラスがたくさんあります。
[Laravel 6.x ファサード：ファサードクラス一覧](https://readouble.com/laravel/6.x/ja/facades.html)

`\Auth`や`\DB`などファサードとして利用したことがある人も多いと思います。


# おわりに
Laravelドキュメントによると、
ファサードのメリットはこのように書かれています。

１つ目。
>ファサードにはたくさんの利点があります。自分で取り込んだり、設定したりする必要があり、長くて覚えにくいクラス名を使わずに、Laravelの機能を簡素で覚えやすい文法で使ってもらえます。

これは、本記事でも言及しました。
いちいちインスタンス化しなくてもいいという点と、
エイリアス登録することで長いクラス名を省略できる
っていうところですね。

２つ目。
>その上に、PHPの動的メソッドのユニークな使用方法のおかげで、簡単にテストができます。

このテストでの利点については
僕自身がまだ実際に経験していないので
今後チェックしていきたいところです。

　
　
そして、デメリットについても言及されています。
>しかしながら、ファサードの使用にはいくつか気をつけるべき点も存在します。ファサードの一番の危険性は、クラスの責任範囲の暴走です。

ファサードが簡単で便利すぎるから、
ついつい１つのクラス内でいろんなファサード使いまくりがち。
そうするとクラスの責任範囲が大きくなりすぎて危険だよ。
っていう話ですね。


今回解説したファサードの仕組みと、
上記のメリットデメリットをしっかり把握したうえで
ファサードを活用していきたいです。

Laravelのちょっと深いところを理解していきたいと思っている方、
これらの記事もおすすめですので是非読んでみてください。
（次に進む前に、LGTMしてもらえるとうれしいです）
[魔法のようなLaravelも素のPHPに始まり素のPHPに終わる。Laravelのライフサイクルを簡単に解説。](https://qiita.com/minato-naka/items/f05b023950ce544a8bec)
[【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)
[【Laravel】引数でタイプヒントしただけでインスタンスがもらえるのはなぜ？Laravelの魔法を解明してみる。](https://qiita.com/minato-naka/items/a4531797af611688db97)
[【Laravel】ファサードとは？何が便利か？どういう仕組みか？](https://qiita.com/minato-naka/items/095f2a1beec1d09f423e)