【Laravel】Facadeとは何か|仕組み・使い方・独自Facadeの作成方法
Cache::get() や Log::info() のような書き方を見ると、「Laravel は static メソッドだらけなのか」「テストしづらくならないのか」と不安になることがあります。見た目は static 呼び出しでも、Laravel の Facade は裏側でサービスコンテナから実体を取り出す仕組みになっています。
この記事では、Facade の基本、サービスコンテナとの関係、公式 Facade の使い方、独自 Facade を作る最小例を整理します。
細かな API は Laravel のメジャーバージョンで変わることがあります。手元の composer.json の laravel/framework に合わせ、Laravel 公式ドキュメントの Facades 章を確認してください。
はじめに
この記事では、Laravel の Facade を初めて読む・使う場面を想定し、次の範囲を扱います。
- Facade が何を隠しているのか
CacheやLogなど公式 Facade の使い方- サービスコンテナとの関係
- テスト時に Facade を mock する考え方
- 独自 Facade を作る最小構成
- 実務でハマりやすい失敗例
背景:Facade は static 風に見える入口
Laravel の公式ドキュメントでは、Facade はサービスコンテナで利用できるクラスに対する static なインターフェースを提供するものとして説明されています。見た目は Cache::get('key') のような static 呼び出しですが、実際には Facade クラスが裏側のオブジェクトへ処理を委譲します。
| 見た目 | 実際の考え方 |
|---|---|
Cache::get('key') | cache サービスをコンテナから取り出して get を呼ぶ |
Log::info('message') | ロガーの実体へ info を委譲する |
Route::get(...) | ルーティング関連の実体へ登録処理を渡す |
実装と検証
公式 Facade を使う
まずは公式 Facade の利用例です。Cache Facade を使うと、キャッシュの読み書きを短く書けます。
<?php
use Illuminate\\Support\\Facades\\Cache;
$value = Cache::remember('dashboard:stats', 60, function () {
return [
'users' => 100,
'orders' => 25,
];
});
Log Facade もよく使われます。
<?php
use Illuminate\\Support\\Facades\\Log;
Log::info('User logged in', [
'user_id' => $user->id,
]);
どちらも static メソッドそのものを定義しているように見えますが、Facade はサービスコンテナ上の実体へ処理を渡しています。このため、Laravel のテスト機能では Facade の fake や mock を使える場面があります。
テストで Facade を mock する
Facade は static 風に見えるため、最初はテストで差し替えづらそうに見えます。Laravel の Facade は、テスト時に shouldReceive で呼び出しを指定できる場面があります。
次の例は、対象コードが Cache::get('dashboard:stats') を 1 回呼ぶ想定です。
<?php
use Illuminate\\Support\\Facades\\Cache;
Cache::shouldReceive('get')
->once()
->with('dashboard:stats')
->andReturn(['users' => 100]);
この例では、実際のキャッシュストアを読まずに戻り値を固定しています。Facade の見た目だけで「テストできない」と判断せず、対象 Facade が mock や fake に対応しているかを確認するとよいです。
Facade の中で何が起きているか
Facade クラスは、多くの場合 Illuminate\\Support\\Facades\\Facade を継承し、getFacadeAccessor でコンテナ上のキーを返します。たとえば公式 Facade の多くは、このアクセサを通じて裏側のサービスを解決します。
getFacadeAccessor() が 'greeting' を返す場合、イメージとしては app('greeting') で取り出した実体へメソッド呼び出しを渡します。ここを押さえると、Facade とサービスコンテナのつながりが見えやすくなります。
独自 Facade のイメージをつかむため、まずは実体となるサービスクラスを作ります。ファイル例は app/Services/GreetingService.php です。
<?php
namespace App\\Services;
class GreetingService
{
public function message(string $name): string
{
return "Hello, {$name}";
}
}
次に、サービスコンテナへ登録します。ここでは、既存の app/Providers/AppServiceProvider.php の register へ書く例を示します。
<?php
namespace App\\Providers;
use App\\Services\\GreetingService;
use Illuminate\\Support\\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton('greeting', function () {
return new GreetingService();
});
}
}
Provider の読み込み方法は Laravel のバージョンや構成で異なります。Laravel 11 以降の構成では bootstrap/providers.php も確認すると、Provider が登録されているか追いやすいです。
最後に Facade クラスを作ります。ファイル例は app/Facades/Greeting.php です。
<?php
namespace App\\Facades;
use Illuminate\\Support\\Facades\\Facade;
class Greeting extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'greeting';
}
}
これで、アプリケーション側から次のように呼び出せます。
<?php
use App\\Facades\\Greeting;
echo Greeting::message('Laravel');
getFacadeAccessor が返す 'greeting' と、Service Provider で登録したキーが一致していることが重要です。ここがずれると、Facade は裏側のサービスを解決できません。
DI と Facade の使い分け
Facade は便利ですが、どこでも使えばよいわけではありません。依存関係をコンストラクタで明示したいクラスでは、DI(依存性注入)を使うと読みやすい場合があります。
<?php
namespace App\\Services;
use Illuminate\\Contracts\\Cache\\Repository as CacheRepository;
class ReportService
{
public function __construct(
private CacheRepository $cache,
) {}
public function summary(): array
{
return $this->cache->remember('report:summary', 60, function () {
return ['total' => 10];
});
}
}
一方で、ルートや小さなアプリケーションコードでは Facade のほうが読みやすい場面もあります。Laravel では Facade と DI のどちらも使えます。
依存を明示したい箇所では DI を選びます。短く書くほうが意図を伝えやすい箇所では Facade を選びます。このように分けると扱いやすいです。
たとえば、ドメインロジックやテスト対象のサービスでは DI を優先します。ルート定義、ログ出力、軽いアプリケーション層の処理では Facade を使うと読みやすいことがあります。
実装時の注意点
独自 Facade を作るときは、サービスコンテナのキー名を雑に決めないほうがよいです。'greeting' のような短いキーはサンプルでは読みやすい一方、実務では他の登録と衝突しない名前を選ぶ必要があります。
また、Facade を多用すると、クラスが何に依存しているかがコンストラクタから見えにくくなります。ドメインロジックやテスト対象のサービスでは、DI を使うほうが意図を追いやすい場合があります。
テストでは Facade の fake / mock を使える場面があります。ただし、すべての Facade を同じ感覚で扱えるとは限りません。公式ドキュメントの Facade Testing を確認し、対象 Facade のテスト方法を合わせてください。
失敗例:アクセサ名とコンテナ登録名がずれていた
筆者が独自 Facade を初めて作ったとき、Service Provider では 'greeting.service' と登録していました。一方で、Facade 側の getFacadeAccessor では 'greeting' を返していました。
コード上は Greeting::message() と書けるため、見た目だけでは原因に気づきにくかったです。
実行時には、コンテナが該当キーのサービスを見つけられず、解決エラーになりました。原因を追うと、Facade クラスではなく Service Provider 側のキー名と一致していないだけでした。
Target class [greeting] does not exist のような解決エラーが出たら、まず登録キーと getFacadeAccessor() の戻り値を見ます。この失敗を避けるには、Facade を作るときに「実体クラス」「コンテナ登録キー」「Facade の getFacadeAccessor」を 3 点セットで確認するとよいです。
学び:Facade は便利な入口だが、実体はコンテナにある
Laravel の Facade は、static 風の入口を提供しつつ、裏側ではサービスコンテナ上の実体へ処理を委譲します。これを理解すると、Cache::get() や Log::info() が「ただの static メソッド」ではないことが見えてきます。
Facade は Laravel らしい書き味を作る便利な仕組みです。一方で、依存関係を明示したい場面では DI のほうが読みやすいこともあります。仕組みを知ったうえで、読み手に優しいほうを選ぶのがよいです。
まとめ
- Facade は static 風の入口であり、裏側ではサービスコンテナ上の実体へ処理を委譲する。
- 独自 Facade では、Service Provider の登録キーと
getFacadeAccessorの戻り値を一致させる。 - DI は依存を明示したい場面、Facade は短く読みたい場面で検討する。
次に試せること
Cache::remember()やLog::info()を使っている箇所を開き、どの Facade を import しているか確認する。- この記事の
GreetingServiceを作り、ルートクロージャや Tinker からGreeting::message('Laravel')を確認する。 - 同じ処理を Facade と DI の両方で書き、どちらが読みやすいかチームで比較する。



