ファサード¶
はじめに¶
Laravelのドキュメントを読んでいると、"ファサード"を介してLaravelの機能と対話するコードの例が見られるでしょう。ファサードは、アプリケーションのサービスコンテナで利用可能なクラスに対する"静的"インターフェースを提供します。Laravelには、Laravelのほぼすべての機能にアクセスできる多くのファサードが付属しています。
Laravelのファサードは、サービスコンテナ内の基礎となるクラスへの"静的プロキシ"として機能し、従来の静的メソッドよりも簡潔で表現力豊かな構文の利点を提供しながら、よりテスト容易性と柔軟性を維持します。ファサードの仕組みを完全に理解していなくても問題ありません。Laravelについて学び続けることが大切です。
Laravelのすべてのファサードは、Illuminate\Support\Facades
名前空間で定義されています。したがって、次のように簡単にファサードにアクセスできます。
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
Laravelのドキュメント全体で、多くの例でファサードを使用してフレームワークのさまざまな機能を説明しています。
ヘルパー関数¶
ファサードに加えて、Laravelはさまざまなグローバル"ヘルパー関数"を提供し、一般的なLaravel機能との対話をさらに容易にします。view
、response
、url
、config
など、よく対話するヘルパー関数がいくつかあります。Laravelが提供する各ヘルパー関数は、対応する機能とともに文書化されていますが、完全なリストは専用のヘルパードキュメントで利用できます。
例えば、Illuminate\Support\Facades\Response
ファサードを使用してJSONレスポンスを生成する代わりに、単にresponse
関数を使用できます。ヘルパー関数はグローバルに利用可能なため、使用するためにクラスをインポートする必要はありません。
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
ファサードをいつ利用するか¶
ファサードには多くの利点があります。簡潔で覚えやすい構文を提供し、長いクラス名を覚えたり手動で注入や設定する必要なく、Laravelの機能を使用できます。さらに、PHPの動的メソッドの独自の使用により、テストが容易です。
ただし、ファサードを使用する際にはいくつかの注意が必要です。ファサードの主な危険性は、クラスの"スコープクリープ"です。ファサードは非常に使いやすく、注入が不要なため、クラスが成長し、1つのクラスで多くのファサードを使用することが容易になります。依存性注入を使用すると、大きなコンストラクタがクラスが大きくなりすぎていることを視覚的に通知します。したがって、ファサードを使用する場合は、クラスのサイズに特に注意を払い、その責任範囲が狭く保たれるようにしてください。クラスが大きくなりすぎる場合は、複数の小さなクラスに分割することを検討してください。
ファサード vs. 依存性注入¶
依存性注入の主な利点の1つは、注入されたクラスの実装を交換できることです。これはテスト中にモックやスタブを注入し、さまざまなメソッドがスタブで呼び出されたことをアサートするのに便利です。
通常、本当の静的クラスメソッドをモックまたはスタブすることはできません。しかし、ファサードは動的メソッドを使用してサービスコンテナから解決されたオブジェクトへメソッド呼び出しをプロキシするため、実際には注入されたクラスインスタンスと同様にファサードをテストできます。例えば、次のルートがあるとします。
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Laravelのファサードテストメソッドを使用して、Cache::get
メソッドが期待する引数で呼び出されたことを確認する次のテストを記述できます。
use Illuminate\Support\Facades\Cache;
test('basic example', function () {
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
});
use Illuminate\Support\Facades\Cache;
/**
* 基本的な機能テストの例
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
ファサード vs. ヘルパー関数¶
ファサードに加えて、Laravelにはビューの生成、イベントの発火、ジョブのディスパッチ、HTTPレスポンスの送信などの一般的なタスクを実行できるさまざまな"ヘルパー"関数が含まれています。多くのヘルパー関数は、対応するファサードと同じ機能を実行します。例えば、このファサード呼び出しとヘルパー呼び出しは同等です。
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
ファサードとヘルパー関数の間に実際的な違いはありません。ヘルパー関数を使用する場合でも、対応するファサードと同様にテストできます。例えば、次のルートがあるとします。
Route::get('/cache', function () {
return cache('key');
});
cache
ヘルパーは、Cache
ファサードの背後にあるクラスのget
メソッドを呼び出します。したがって、ヘルパー関数を使用していても、次のテストを記述して、メソッドが期待する引数で呼び出されたことを確認できます。
use Illuminate\Support\Facades\Cache;
/**
* 基本的な機能テストの例
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
ファサードの仕組み¶
Laravelアプリケーションでは、ファサードはコンテナからのオブジェクトへのアクセスを提供するクラスです。これを機能させる仕組みは、Facade
クラスにあります。Laravelのファサードと作成するカスタムファサードは、すべて基本クラスIlluminate\Support\Facades\Facade
を拡張します。
Facade
基本クラスは、__callStatic()
マジックメソッドを使用して、ファサードからコンテナから解決されたオブジェクトへの呼び出しを遅延します。以下の例 では、Laravelのキャッシュシステムへの呼び出しが行われます。このコードを見ると、Cache
クラスの静的get
メソッドが呼び出されていると思われるかもしれません。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* 指定されたユーザーのプロフィールを表示します。
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
ファイルの上部近くで、Cache
ファサードを"インポート"していることに注意してください。このファサードは、Illuminate\Contracts\Cache\Factory
インターフェースの基礎となる実装へのプロキシとして機能します。ファサードを使用して行うすべての呼び出しは、Laravelのキャッシュサービスの基礎となるインスタンスに渡されます。
Illuminate\Support\Facades\Cache
クラスを見ると、静的メソッドget
がないことがわかります。
class Cache extends Facade
{
/**
* コンポーネントの登録名を取得します。
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}
代わりに、Cache
ファサードは基本クラスFacade
を拡張し、getFacadeAccessor()
メソッドを定義します。このメソッドの役割は、サービスコンテナのバインディング名を返すことです。ユーザーがCache
ファサードの任意の静的メソッドを参照すると、Laravelはサービスコンテナからcache
バインディングを解決し、要求されたメソッド(この場合はget
)をそのオブジェクトに対して実行します。
リアルタイムファサード¶
リアルタイムファサードを使用すると、アプリケーション内の任意のクラスをファサードのように扱うことができます。これがどのように使用できるかを説明するために、まずリアルタイムファサードを使用しないコードを見てみましょう。例えば、Podcast
モデルにpublish
メソッドがあるとします。ただし、ポッドキャストを公開するために、Publisher
インスタンスを注入する必要があります。
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* ポッドキャストを公開します。
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
メソッドにパブリッシャー実装を注入することで、注入されたパブリッシャーをモックすることでメソッドを個別に簡単にテストできます。ただし、publish
メソッドを呼び出すたびにパブリッシャーインスタンスを常に渡す必要があります。リアルタイムファサードを使用すると、同じテスト容易性を維持しながら、明示的にPublisher
インスタンスを渡す必要がなくなります。リアルタイムファサードを生成するには、インポートしたクラスの名前空間にFacades
をプレフィックスとして付けます。
<?php
namespace App\Models;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* ポッドキャストを公開します。
*/
public function publish(): void
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
use Facades\App\Contracts\Publisher; // [tl! add]
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* ポッドキャストを公開する。
*/
public function publish(): void // [tl! add]
{
$this->update(['publishing' => now()]);
Publisher::publish($this); // [tl! add]
}
}
リアルタイムファサードが使用されると、パブリッシャーの実装は、Facades
プレフィックスの後に現れるインターフェースまたはクラス名の部分を使用して、サービスコンテナから解決されます。テスト時には、Laravelの組み込みファサードテストヘルパーを使用して、このメソッド呼び出しをモックすることができます。
<?php
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('ポッドキャストを公開できる', function () {
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
});
<?php
namespace Tests\Feature;
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* テスト例。
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
ファサードクラスリファレンス¶
以下に、すべてのファサードとその基礎となるクラスを示します。これは、特定のファサードルートのAPIドキュメントをすばやく掘り下げるのに便利なツールです。該当する場合は、サービスコンテナのバインディングキーも含まれています。