Facades
Table of Contents
Introduction
Laravel のドキュメンテーション全体を通じて、Laravel の機能とやり取りする"facades"を介したコードの例を見ることができます。"facades"は、application のservice containerで利用可能な classes への"静的"なインターフェースを提供します。Laravel は、Laravel のほとんどすべての機能にアクセスを提供する多くの"facades"と共に提供されています。
Laravel facades は、"static proxies"として機能し、 service container 内の基礎となるクラスに対する短く、表現豊かな syntax の利点を提供しつつ、伝統的な静的メソッドよりも高いテスト可能性と柔軟性を維持します。 facades の動作を完全に理解していなくても問題ありません - 流れに身を任せて Laravel について学習を続けてください。
Laravel の全ての facades は、Illuminate\Support\Facades
名前空間で定義されています。そのため、以下のように簡単に facade にアクセスすることができます:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
Laravel のドキュメンテーション全体で、多くの例が facades を使用してフレームワークのさまざまな features を示しています。
ヘルパー関数
facades を補完するために、 Laravel は多種多様なグローバル "ヘルパー関数"を提供しており、これにより一般的な Laravel features とより簡単にやり取りすることが可能になります。ご利用になる可能性がある一般的なヘルパー関数には、view
、response
、url
、config
などがあります。各 Laravel が提供するヘルパー関数は、それぞれの機能とともに文書化されていますが、完全なリストは専用のヘルパードキュメンテーション内でご覧いただけます。
たとえば、Illuminate\Support\Facades\Response
facade を使って JSON response を生成する代わりに、単純にresponse
関数を使用することができます。ヘルパ関数はグローバルで利用可能なので、それらを使用するために classes をインポートする必要はありません:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
When to Utilize Facades
Facades には多くの利点があります。それらは、手短で覚えやすい syntax を提供し、Laravel の features を長い class 名を記憶したり、手動で注入したり設定したりすることなく使用することを可能にします。さらに、PHP の動的メソッドを unique に使用することで、テストが容易になります。
しかし、facades を使用する際には一定の注意が必要です。facades の primary な危険性は、class の scope creep です。facades は使用が非常に容易で、注入を必要としないため、class を増やし続けたり、single class で多くの facades を使用することが容易になります。dependency injection を使用すると、大きなコンストラクタがあなたに class が大きくなりすぎていることを視覚的にフィードバックしてくれるため、この可能性は緩和されます。したがって、facades を使用する際には、class のサイズに特に注意を払い、その scope の範囲が狭く保たれるようにしてください。もし class が大きすぎると感じたら、それを複数の小さな class に分割することを検討してください。
Facades 対 Dependency Injection
dependency injection の primary な利点の一つは、注入された class の実装を swap する能力です。これは testing 中に便利で、モックやスタブを注入して、スタブ上でさまざまなメソッドが assert されたことを確認できます。
通常、本当に静的な class method をモックまたはスタブにすることはできません。しかし、 facades が service container から解決されたオブジェクトに method 呼び出しをプロキシするための動的なメソッドを使用するため、私たちは facades を注入された class インスタンスをテストするのと同じようにテストできます。例えば、以下の route が与えられた場合:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Laravel の facade testing メソッドを使って、私たちは以下のテストを書くことで、Cache::get
method が我々が期待する引数で呼ばれたかどうかを確認することができます:
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;
/**
* A basic functional test example.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
Facades 対 ヘルパー関数
facades に加えて、" Laravel "には、 views の生成、 events の発火、 jobs のディスパッチ、または HTTP responses の送信など、一般的なタスクを実行できる「ヘルパー」関数が多数含まれています。これらのヘルパー関数の多くは、対応する facade と同じ機能を果たします。例えば、この" facade "コールとヘルパーコールは同等です:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
facade とヘルパー関数の間には全く実質的な違いはありません。ヘルパー関数を使用する場合でも、それに対応する facades と全く同じようにテストできます。たとえば、次の route をご覧ください:
Route::get('/cache', function () {
return cache('key');
});
Cache
helper は、cache
facade の基礎となる class で get
method を呼び出す予定です。したがって、helper 関数を使用していても、期待した引数で method が呼び出されたことを確認するための次の test を書くことができます:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*/
public function test_basic_example(): void
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
How Facades Work
Laravel application において、 facade は、コンテナから object へのアクセスを提供する class です。これを可能にする仕組みは、Facade
class にあります。Laravel の facades および作成する custom facades は、基底のIlluminate\Support\Facades\Facade
class を拡張します。
Facade
ベースの class では、__callStatic()
マジックメソッドを使って、あなたの facade からコンテナから解決された object への呼び出しを遅延させます。以下の例では、Laravel cache システムへの呼び出しが行われています。このコードを一見すると、静的なget
method がCache
class 上で呼び出されていると思うかもしれません:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*/
public function showProfile(string $id): View
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
ファイルの上部付近で、私たちは Cache
facade を "インポート" していることに注意してください。この facade は、 Illuminate\Contracts\Cache\Factory
インターフェースの基本実装へのアクセスを代理する役割を果たします。 facade を使って行うどんな呼び出しも、Laravel の cache service の基本インスタンスに渡されます。
そのIlluminate\Support\Facades\Cache
class を見てみると、get
という静的な method が存在しないことがわかります。
class Cache extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'cache';
}
}
それどころか、Cache
facade は基本的なFacade
class を拡張し、 job getFacadeAccessor()
を定義します。この method の仕事は、 service container のバインディングの名前を返すことです。 user がCache
facade の任意の静的 method を参照するとき、 Laravel は“ service containerからcache
バインディングを解決し、要求された method (この場合はget
)をその object に対して実行します。
Real-Time Facades
リアルタイムの facades を使用すると、 application 内の任意の class をまるで facade であるかのように扱うことができます。これがどのように使用されるかを説明するために、まずはリアルタイムの facades を使用しない何らかの code を見てみましょう。例えば、私たちの Podcast
model が publish
method を持っていると仮定しましょう。しかし、ポッドキャストを publish するためには、Publisher
インスタンスを注入する必要があります。
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
method にパブリッシャー実装を注入することで、注入されたパブリッシャーをモックすることができ、method を単独で簡単にテストすることができます。しかし、publish
メソッドを呼び出すたびにパブリッシャーインスタンスを常に渡す必要があります。リアルタイム facades を使用すると、同じテストの容易さを保ちながら、Publisher
インスタンスを明示的に渡す必要がなくなります。リアルタイム facade を生成するには、インポートされた class の名前空間に Facades
をプレフィックスとして追加します。
<?php
namespace App\Models;
use App\Contracts\Publisher; // [tl! remove]
use Facades\App\Contracts\Publisher; // [tl! add]
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*/
public function publish(Publisher $publisher): void // [tl! remove]
public function publish(): void // [tl! add]
{
$this->update(['publishing' => now()]);
$publisher->publish($this); // [tl! remove]
Publisher::publish($this); // [tl! add]
}
}
リアルタイムの facade を使用すると、publisher の実装はインターフェースの一部または class 名を用いて、Facades
プレフィックスの後に現れる service container から解決されます。 testing を行う際には、Laravel の組み込みの facade testing helpers を使ってこの method 呼び出しをモックできます。
<?php
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('podcast can be published', 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;
/**
* A test example.
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
Facade Class Reference
以下に、すべての facade とその下にある class を見つけることができます。これは特定の facade root のための API ドキュメンテーションを素早く探るのに役立つツールです。該当する場合には、service container bindingキーも含まれています。