LaravelのFacadeを最近使うようになったので、勉強ついでにFacadeの使い方についてまとめてみようかなと思います。
Laravelのファサードとは
デザインパターンにもファサードパターンというものがあるので、紛らわしいですが、今回はLaravelのファサードです。
以下は今時点での最新の8.xのドキュメントになります。
ファサードは、アプリケーションのサービスコンテナで使用可能なクラスに対して「静的な」インターフェイスを提供します。Laravelは、Laravelのほとんどすべての機能へのアクセスを提供する多くのファサードを提供しています。
Laravelファサードは、サービスコンテナ内の基礎となるクラスへの「静的プロキシ」として機能し、従来の静的メソッドよりもテスト容易性と柔軟性を維持しながら、簡潔で表現力豊かな構文という利点を提供しています。ファサードが内部でどのように機能するかを完全に理解していなくても、まったく問題ありません。流れに沿って、Laravelについて学び続けてください。
また、6.xのドキュメントにはこのように書かれています。
ファサード(facade、「入り口」)はアプリケーションのサービスコンテナに登録したクラスへ、「静的」なインターフェイスを提供します。Laravelのほとんどの機能に対して、ファサードが用意されています。Laravelの「ファサード」は、サービスコンテナ下で動作しているクラスに対し、"static proxy"として動作しています。これにより伝統的な静的メソッドよりもテストの行いやすさと柔軟性を保ちながらも、簡潔で記述的であるという利点があります。
ということは、Facadeというものは入り口みたいなもので、内部的にいろんな処理をしているかもしれないが、使う側は呼び方だけ知っていれば良い、みたいなことでしょうか。
例えば、以下のようなコードもRouteファサードやCacheファサードを使用しているということですね。
<?php use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Route; Route::get('/cache', function () { return Cache::get('key'); });
ドキュメントを読むとファサードを使う理由は以下になりそうです。
- 長いクラス名を覚えていなくても、Laravelの機能を使用できる
- PHPの動的メソッドを独自に使用しているのでテストが簡単
- 依存注入を必要としない
なので、以下のようにモックを使用したテストが書けるということですね。(ドキュメントより)
<?php use Illuminate\Support\Facades\Cache; /** * 基本的な機能テストの例 * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $response = $this->get('/cache'); $response->assertSee('value'); }
実際にFacadeを実装してみる
さて、早速Facadeを利用してみたいと思います。
Facadeクラスを作成する
まずは、呼ばれるFacadeクラスを作成します。
<?php namespace App\Support\Facades; use Illuminate\Support\Facades\Facade; /** * @method static void fuga() */ class Hoge extends Facade { protected static function getFacadeAccessor() { return 'hoge'; } }
このように、定義します。
また、クラスのPHPDocには、今回呼ばれるfugaメソッドをstaticで定義させておきます。こうすることで、今回作成するFacadeを利用するときに、エディタが予測・補完してくれるようになります。(対応しているエディタであれば)
こうすることで、サービスコンテナでhoge
の依存解決をしてそのオブジェクトに対してfugaメソッドが実行されるようです。なので、今回はこれ用にサービスプロバイダを作成して、処理を呼び出すようにしてみます。
呼ばれる側のインターフェースの作成
まずは、実行される処理が書かれたクラスのインターフェースを作ってあげます。
<?php namespace App\Contracts\Hoge; interface HogeInterface { public function fuga(): void; }
クラスの実装
定義されたインターフェースをもとに実装していきます。
<?php namespace App\Contracts\Hoge; class Hoge implements HogeInterface { public function fuga(): void { // 処理を書く } }
上で定義したインターフェースを実装していく形で作成していきます。
サービスプロバイダを作成する
次にサービスプロバイダを作っていきます。
<?php namespace App\Providers\Facades; use App\Contracts\Hoge\Hoge; use Illuminate\Support\ServiceProvider; class HogeServiceProvider extends ServiceProvider { public function register(): void { $this->app->singleton('hoge', Hoge::class); } }
後は、config/app.phpに作成したServiceProviderを追加します。
<?php return [ // 省略 'providers' => [ // 省略 App\Providers\Facades\HogeServiceProvider::class, // 省略 ], // 省略 ];
最後にFacadeを呼び出す
これで準備は完了しました。後は処理を呼ぶだけです。
<?php use App\Support\Facades\Hoge; // なにかしらの処理の途中 Hoge::fuga();
これで、無事処理を呼ぶことができました!
さいごに
このようにして、処理から分離して注入が不要なクラスを作成することができました。これで、内部処理をあまり理解しなくても利用できる状態になりましたね。