LoginSignup
295
244

More than 1 year has passed since last update.

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

Last updated at Posted at 2020-03-11

はじめに

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

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


本記事内ではサービスコンテナを利用したコードが多数登場しますが
サービスコンテナ自体の仕組みは解説しません。
サービスコンテナについてはこちらの記事で解説しています。
【Laravel】サービスコンテナとは?2つの強力な武器を持ったインスタンス化マシーン。簡単に解説。

概要

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

こんな風にできる。

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

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

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

ファサードの作り方

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

元クラス作成

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

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

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

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

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

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

サービスコンテナに登録

ファサードとして利用したいクラスは、
サービスコンテナに登録します。1

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

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

ファサードクラス作成

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

FacadeClassA.php
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.phpaliases配列に
自作ファサードの設定を追記します。

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

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

そして、
FacadeClassA::methodA();

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

インスタンス化の手間を削減

長ったらしい名前空間を記述する手間削減
っていうことですね。

ファサードの仕組み

\FacadeClassA::methodA();

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

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

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

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

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

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();の中を見てみます。

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

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

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

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

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

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

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

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

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()を実行しているのが分かります。

\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】ファサードとは?何が便利か?どういう仕組みか?

  1. サービスコンテナに登録しなくてもgetFacadeAccessor()でそのままクラス名を返せば利用可能ですが、通常はサービスコンテナに登録することが多い
     

295
244
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
295
244