メインコンテンツまでスキップ

ファサード

はじめに

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の機能とのやり取りをさらに簡単にします。対話する可能性のある一般的なヘルパー関数には、viewresponseurlconfigなどがあります。Laravelが提供する各ヘルパー関数は、それに対応する機能とともに文書化されていますが、完全なリストは専用のヘルパーのドキュメントで利用可能です。

たとえば、JSONレスポンスを生成するためにIlluminate\Support\Facades\Responseファサードを使用する代わりに、単純に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;

/**
* 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');
}

ファサード 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;

/**
* 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');
}

ファサードの動作

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
{
/**
* Show the profile for the given user.
*/
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
{
/**
* Get the registered name of the component.
*/
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
{
/**
* Publish the podcast.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);

$publisher->publish($this);
}
}

メソッドにパブリッシャーの実装をインジェクトすることで、インジェクトされたパブリッシャーをモックすることができるため、メソッドを独立して簡単にテストすることができます。ただし、publishメソッドを呼び出すたびに常にパブリッシャーインスタンスを渡す必要があります。リアルタイムファサードを使用すると、Publisherインスタンスを明示的に渡す必要はないまま、同じテスト可能性を維持できます。リアルタイムファサードを生成するには、インポートされたクラスの名前空間に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]
}
}

リアルタイムファサードを使用すると、パブリッシャーの実装はFacades接頭辞の後に表示されるインターフェースまたはクラス名の一部を使用してサービスコンテナから解決されます。テスト時には、Laravelの組込みファサードテストヘルパーを使用してこのメソッド呼び出しをモックすることができます:

<?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();
}
}

ファサードクラスリファレンス

以下に、各ファサードとその基礎となるクラスが示されています。これは、特定のファサードルートのAPIドキュメントに迅速にアクセスするための便利なツールです。適用可能な場合、サービスコンテナバインディングキーも含まれています。

ファサードクラスサービスコンテナバインディング
AppIlluminate\Foundation\Applicationapp
ArtisanIlluminate\Contracts\Console\Kernelartisan
AuthIlluminate\Auth\AuthManagerauth
Auth (Instance)Illuminate\Contracts\Auth\Guardauth.driver
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BroadcastIlluminate\Contracts\Broadcasting\Factory 
Broadcast (Instance)Illuminate\Contracts\Broadcasting\Broadcaster 
BusIlluminate\Contracts\Bus\Dispatcher 
CacheIlluminate\Cache\CacheManagercache
Cache (Instance)Illuminate\Cache\Repositorycache.store
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DateIlluminate\Support\DateFactorydate
DBIlluminate\Database\DatabaseManagerdb
DB (Instance)Illuminate\Database\Connectiondb.connection
EventIlluminate\Events\Dispatcherevents
ExceptionsIlluminate\Foundation\Exceptions\Handler 
Exceptions (Instance)Illuminate\Contracts\Debug\ExceptionHandler 
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate 
HashIlluminate\Contracts\Hashing\Hasherhash
HttpIlluminate\Http\Client\Factory 
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\LogManagerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager 
PasswordIlluminate\Auth\Passwords\PasswordBrokerManagerauth.password
Password (Instance)Illuminate\Auth\Passwords\PasswordBrokerauth.password.broker
Pipeline (Instance)Illuminate\Pipeline\Pipeline 
ProcessIlluminate\Process\Factory 
QueueIlluminate\Queue\QueueManagerqueue
Queue (Instance)Illuminate\Contracts\Queue\Queuequeue.connection
Queue (Base Class)Illuminate\Queue\Queue 
RateLimiterIlluminate\Cache\RateLimiter 
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\RedisManagerredis
Redis (Instance)Illuminate\Redis\Connections\Connectionredis.connection
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory 
Response (Instance)Illuminate\Http\Response 
RouteIlluminate\Routing\Routerrouter
ScheduleIlluminate\Console\Scheduling\Schedule 
SchemaIlluminate\Database\Schema\Builder 
SessionIlluminate\Session\SessionManagersession
Session (Instance)Illuminate\Session\Storesession.store
StorageIlluminate\Filesystem\FilesystemManagerfilesystem
Storage (Instance)Illuminate\Contracts\Filesystem\Filesystemfilesystem.disk
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator (Instance)Illuminate\Validation\Validator 
ViewIlluminate\View\Factoryview
View (Instance)Illuminate\View\View 
ViteIlluminate\Foundation\Vite