
# 门面（Facades）

-   [简介](#introduction)
-   [何时使用 Facades](#when-to-use-facades)
    -   [Facades 与依赖注入](#facades-vs-dependency-injection)
    -   [Facades 与辅助函数](#facades-vs-helper-functions)
-   [Facades 的工作原理](#how-facades-work)
-   [实时 Facades](#real-time-facades)
-   [Facade 类参考](#facade-class-reference)

## 简介

在整个 Laravel 文档中，你会看到一些通过“facades（门面）”与 Laravel 功能交互的代码示例。Facades 为应用程序 [服务容器](/docs/laravel/12.x/container) 中可用的类提供了一个“静态（static）”接口。Laravel 自带了许多 facades，它们提供了对 Laravel 几乎所有功能的访问。

Laravel facades 充当服务容器中底层类的“静态代理（static proxies）”，在保持比传统静态方法更好的可测试性和灵活性的同时，还提供了简洁且富有表现力的语法。即使你还没有完全理解 facades 的工作原理也没关系——顺其自然，继续学习 Laravel 即可。

Laravel 的所有 facades 都定义在 `Illuminate\Support\Facades` 命名空间中。因此，我们可以像下面这样轻松访问一个 facade：

```php
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;

Route::get('/cache', function () {
    return Cache::get('key');
});
```

在整个 Laravel 文档中，许多示例都会使用 facades 来演示框架的各种功能。

#### 辅助函数（Helper Functions）

为了补充 facades，Laravel 提供了各种全局“辅助函数（helper functions）”，使与常见 Laravel 功能交互变得更加简单。你可能会接触到的一些常见辅助函数包括 `view`、`response`、`url`、`config` 等。Laravel 提供的每个辅助函数都会在其对应功能的文档中进行说明；不过，你也可以在专门的 [辅助函数文档](/docs/laravel/12.x/helpers) 中查看完整列表。



例如，与其使用 `Illuminate\Support\Facades\Response` facade 来生成 JSON 响应，我们可以直接使用 `response` 函数。由于辅助函数是全局可用的，因此你无需导入任何类即可使用它们：

```php
use Illuminate\Support\Facades\Response;

Route::get('/users', function () {
    return Response::json([
        // ...
    ]);
});

Route::get('/users', function () {
    return response()->json([
        // ...
    ]);
});
```

## 何时使用 Facades

Facades 有许多优点。它们提供了简洁且易记的语法，使你能够使用 Laravel 的功能，而无需记住那些必须手动注入或配置的长类名。此外，由于它们对 PHP 动态方法的独特使用方式，也使得它们更容易进行测试。

不过，在使用 facades 时也需要谨慎。facades 的主要风险是类的“职责蔓延（scope creep）”。由于 facades 非常容易使用且不需要注入，因此很容易让你的类不断增长，并在单个类中使用许多 facades。使用依赖注入时，大型构造函数会通过可视化反馈提醒你类正在变得过于庞大，从而减轻这种风险。因此，在使用 facades 时，请特别注意类的大小，以便让其职责范围保持单一。如果你的类变得过大，可以考虑将其拆分成多个较小的类。

### Facades 与依赖注入

依赖注入的一个主要优点是能够替换被注入类的实现。这在测试期间非常有用，因为你可以注入一个 mock 或 stub，并断言（assert）是否调用了 stub 上的各种方法。



通常情况下，真正的静态类方法是无法被 mock 或 stub 的。然而，由于 facades 使用动态方法将方法调用代理到从服务容器中解析出来的对象，因此我们实际上可以像测试注入的类实例一样测试 facades。例如，给定以下路由：

```php
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});
```

使用 Laravel 的 facade 测试方法，我们可以编写如下测试，以验证 `Cache::get` 方法是否使用了我们预期的参数进行调用：

```php tab=Pest
use Illuminate\Support\Facades\Cache;

test('basic example', function () {
    Cache::shouldReceive('get')
        ->with('key')
        ->andReturn('value');

    $response = $this->get('/cache');

    $response->assertSee('value');
});
```

```php tab=PHPUnit
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');
}
```

### Facades 与辅助函数

除了 facades 之外，Laravel 还包含各种“辅助函数（helper）”，可用于执行常见任务，例如生成视图、触发事件、分发任务或发送 HTTP 响应。这些辅助函数中的许多都执行与相应 facade 相同的功能。例如，下面的 facade 调用与 helper 调用是等价的：

```php
return Illuminate\Support\Facades\View::make('profile');

return view('profile');
```

facades 与辅助函数之间实际上没有任何实质性的区别。使用辅助函数时，你仍然可以像测试对应 facade 一样对其进行测试。例如，给定以下路由：

```php
Route::get('/cache', function () {
    return cache('key');
});
```



`cache` 辅助函数将会调用 `Cache` facade 底层类上的 `get` 方法。因此，即使我们使用的是辅助函数，我们仍然可以编写如下测试，以验证该方法是否使用了我们预期的参数进行调用：

```php
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');
}
```

## Facades 的工作原理

在 Laravel 应用程序中，facade 是一个提供从容器中访问对象能力的类。使其能够工作的机制位于 `Facade` 类中。Laravel 的 facades，以及你创建的任何自定义 facades，都将继承基础类 `Illuminate\Support\Facades\Facade`。

`Facade` 基类使用 `__callStatic()` 魔术方法，将对 facade 的调用延迟转发到从容器中解析出的对象。在下面的示例中，调用了 Laravel 缓存系统。仅通过查看这段代码，人们可能会认为是在 `Cache` 类上调用静态 `get` 方法：

```php
<?php

namespace App\Http\Controllers;

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]);
    }
}
```

请注意，在文件顶部我们“导入（import）”了 `Cache` facade。该 facade 作为代理，用于访问 `Illuminate\Contracts\Cache\Factory` 接口的底层实现。我们通过 facade 发出的任何调用，都会被传递到底层的 Laravel 缓存服务实例。



如果我们查看 `Illuminate\Support\Facades\Cache` 类，你会发现其中并没有静态 `get` 方法：

```php
class Cache extends Facade
{
    /**
     * 获取组件已注册的名称。
     */
    protected static function getFacadeAccessor(): string
    {
        return 'cache';
    }
}
```

相反，`Cache` facade 继承了基础 `Facade` 类，并定义了 `getFacadeAccessor()` 方法。这个方法的作用是返回服务容器绑定的名称。当用户在 `Cache` facade 上调用任何静态方法时，Laravel 会从 [服务容器](/docs/laravel/12.x/container) 中解析 `cache` 绑定，并在该对象上运行所请求的方法（在本例中为 `get`）。

## 实时 Facades

使用实时 facades，你可以将应用程序中的任何类当作 facade 来使用。为了说明它的用法，我们先来看一段未使用实时 facades 的代码。例如，假设我们的 `Podcast` 模型有一个 `publish` 方法。不过，为了发布 podcast，我们需要注入一个 `Publisher` 实例：

```php
<?php

namespace App\Models;

use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * 发布 podcast。
     */
    public function publish(Publisher $publisher): void
    {
        $this->update(['publishing' => now()]);

        $publisher->publish($this);
    }
}
```

将 publisher 的实现注入到方法中，可以让我们轻松地对该方法进行独立测试，因为我们可以 mock 被注入的 publisher。不过，这也要求我们每次调用 `publish` 方法时都必须传递一个 publisher 实例。

使用实时 facades，我们既可以保持相同的可测试性，又无需显式地传递 `Publisher` 实例。要生成一个实时 facade，只需在导入类的命名空间前添加 `Facades` 前缀：

```php
<?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
{
    /**
     * 发布 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 的实现将通过 `Facades` 前缀之后的接口或类名部分，从服务容器中解析出来。在测试时，我们可以使用 Laravel 内置的 facade 测试辅助工具来 mock 这个方法调用：

```php tab=Pest
<?php

use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()->use(RefreshDatabase::class);

test('podcast can be published', function () {
    $podcast = Podcast::factory()->create();

    Publisher::shouldReceive('publish')->once()->with($podcast);

    $podcast->publish();
});
```

```php tab=PHPUnit
<?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();
    }
}
```

## Facade 类参考

下面你将看到每个 facade 及其底层类。这是一个有用的工具，可以帮助你快速查阅给定 facade 根类的 API 文档。在适用的情况下，也包含了 [服务容器绑定](/docs/laravel/12.x/container) 键。

<div class="overflow-auto">

| Facade | Class | Service Container Binding |
| --- | --- | --- |
| App | [Illuminate\Foundation\Application](https://api.laravel.com/docs/laravel/12.x/Illuminate/Foundation/Application.html) | `app` |
| Artisan | [Illuminate\Contracts\Console\Kernel](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Console/Kernel.html) | `artisan` |
| Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` |
| Auth | [Illuminate\Auth\AuthManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Auth/AuthManager.html) | `auth` |
| Blade | [Illuminate\View\Compilers\BladeCompiler](https://api.laravel.com/docs/laravel/12.x/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` |
| Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Broadcasting/Broadcaster.html) | &nbsp; |
| Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Broadcasting/Factory.html) | &nbsp; |
| Bus | [Illuminate\Contracts\Bus\Dispatcher](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Bus/Dispatcher.html) | &nbsp; |
| Cache (Instance) | [Illuminate\Cache\Repository](https://api.laravel.com/docs/laravel/12.x/Illuminate/Cache/Repository.html) | `cache.store` |
| Cache | [Illuminate\Cache\CacheManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Cache/CacheManager.html) | `cache` |
| Config | [Illuminate\Config\Repository](https://api.laravel.com/docs/laravel/12.x/Illuminate/Config/Repository.html) | `config` |
| Context | [Illuminate\Log\Context\Repository](https://api.laravel.com/docs/laravel/12.x/Illuminate/Log/Context/Repository.html) | &nbsp; |
| Cookie | [Illuminate\Cookie\CookieJar](https://api.laravel.com/docs/laravel/12.x/Illuminate/Cookie/CookieJar.html) | `cookie` |
| Crypt | [Illuminate\Encryption\Encrypter](https://api.laravel.com/docs/laravel/12.x/Illuminate/Encryption/Encrypter.html) | `encrypter` |
| Date | [Illuminate\Support\DateFactory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Support/DateFactory.html) | `date` |
| DB (Instance) | [Illuminate\Database\Connection](https://api.laravel.com/docs/laravel/12.x/Illuminate/Database/Connection.html) | `db.connection` |
| DB | [Illuminate\Database\DatabaseManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Database/DatabaseManager.html) | `db` |
| Event | [Illuminate\Events\Dispatcher](https://api.laravel.com/docs/laravel/12.x/Illuminate/Events/Dispatcher.html) | `events` |
| Exceptions (Instance) | [Illuminate\Contracts\Debug\ExceptionHandler](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Debug/ExceptionHandler.html) | &nbsp; |
| Exceptions | [Illuminate\Foundation\Exceptions\Handler](https://api.laravel.com/docs/laravel/12.x/Illuminate/Foundation/Exceptions/Handler.html) | &nbsp; |
| File | [Illuminate\Filesystem\Filesystem](https://api.laravel.com/docs/laravel/12.x/Illuminate/Filesystem/Filesystem.html) | `files` |
| Gate | [Illuminate\Contracts\Auth\Access\Gate](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Auth/Access/Gate.html) | &nbsp; |
| Hash | [Illuminate\Contracts\Hashing\Hasher](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Hashing/Hasher.html) | `hash` |
| Http | [Illuminate\Http\Client\Factory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Http/Client/Factory.html) | &nbsp; |
| Lang | [Illuminate\Translation\Translator](https://api.laravel.com/docs/laravel/12.x/Illuminate/Translation/Translator.html) | `translator` |
| Log | [Illuminate\Log\LogManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Log/LogManager.html) | `log` |
| Mail | [Illuminate\Mail\Mailer](https://api.laravel.com/docs/laravel/12.x/Illuminate/Mail/Mailer.html) | `mailer` |
| Notification | [Illuminate\Notifications\ChannelManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Notifications/ChannelManager.html) | &nbsp; |
| Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://api.laravel.com/docs/laravel/12.x/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` |
| Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` |
| Pipeline (Instance) | [Illuminate\Pipeline\Pipeline](https://api.laravel.com/docs/laravel/12.x/Illuminate/Pipeline/Pipeline.html) | &nbsp; |
| Process | [Illuminate\Process\Factory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Process/Factory.html) | &nbsp; |
| Queue (Base Class) | [Illuminate\Queue\Queue](https://api.laravel.com/docs/laravel/12.x/Illuminate/Queue/Queue.html) | &nbsp; |
| Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` |
| Queue | [Illuminate\Queue\QueueManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Queue/QueueManager.html) | `queue` |
| RateLimiter | [Illuminate\Cache\RateLimiter](https://api.laravel.com/docs/laravel/12.x/Illuminate/Cache/RateLimiter.html) | &nbsp; |
| Redirect | [Illuminate\Routing\Redirector](https://api.laravel.com/docs/laravel/12.x/Illuminate/Routing/Redirector.html) | `redirect` |
| Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://api.laravel.com/docs/laravel/12.x/Illuminate/Redis/Connections/Connection.html) | `redis.connection` |
| Redis | [Illuminate\Redis\RedisManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Redis/RedisManager.html) | `redis` |
| Request | [Illuminate\Http\Request](https://api.laravel.com/docs/laravel/12.x/Illuminate/Http/Request.html) | `request` |
| Response (Instance) | [Illuminate\Http\Response](https://api.laravel.com/docs/laravel/12.x/Illuminate/Http/Response.html) | &nbsp; |
| Response | [Illuminate\Contracts\Routing\ResponseFactory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Routing/ResponseFactory.html) | &nbsp; |
| Route | [Illuminate\Routing\Router](https://api.laravel.com/docs/laravel/12.x/Illuminate/Routing/Router.html) | `router` |
| Schedule | [Illuminate\Console\Scheduling\Schedule](https://api.laravel.com/docs/laravel/12.x/Illuminate/Console/Scheduling/Schedule.html) | &nbsp; |
| Schema | [Illuminate\Database\Schema\Builder](https://api.laravel.com/docs/laravel/12.x/Illuminate/Database/Schema/Builder.html) | &nbsp; |
| Session (Instance) | [Illuminate\Session\Store](https://api.laravel.com/docs/laravel/12.x/Illuminate/Session/Store.html) | `session.store` |
| Session | [Illuminate\Session\SessionManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Session/SessionManager.html) | `session` |
| Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://api.laravel.com/docs/laravel/12.x/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` |
| Storage | [Illuminate\Filesystem\FilesystemManager](https://api.laravel.com/docs/laravel/12.x/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` |
| URL | [Illuminate\Routing\UrlGenerator](https://api.laravel.com/docs/laravel/12.x/Illuminate/Routing/UrlGenerator.html) | `url` |
| Validator (Instance) | [Illuminate\Validation\Validator](https://api.laravel.com/docs/laravel/12.x/Illuminate/Validation/Validator.html) | &nbsp; |
| Validator | [Illuminate\Validation\Factory](https://api.laravel.com/docs/laravel/12.x/Illuminate/Validation/Factory.html) | `validator` |
| View (Instance) | [Illuminate\View\View](https://api.laravel.com/docs/laravel/12.x/Illuminate/View/View.html) | &nbsp; |
| View | [Illuminate\View\Factory](https://api.laravel.com/docs/laravel/12.x/Illuminate/View/Factory.html) | `view` |
| Vite | [Illuminate\Foundation\Vite](https://api.laravel.com/docs/laravel/12.x/Illuminate/Foundation/Vite.html) | &nbsp; |

</div>

