跳至内容

Facades

介绍

在整个 Laravel 文档中,您将看到通过“Facades”与 Laravel 功能交互的代码示例。Facades 为应用程序服务容器中可用的类提供了一个“静态”接口。Laravel 内置了许多 Facades,可以访问几乎所有的 Laravel 功能。

Laravel Facades 充当服务容器中底层类的“静态代理”,提供简洁、富有表现力的语法,同时比传统的静态方法保持更高的可测试性和灵活性。如果您不完全理解 Facades 的工作原理,也没关系,只需顺其自然,继续学习 Laravel 即可。

Laravel 的所有 Facade 都定义在Illuminate\Support\Facades命名空间中。因此,我们可以轻松地像这样访问 Facade:

1use Illuminate\Support\Facades\Cache;
2use Illuminate\Support\Facades\Route;
3 
4Route::get('/cache', function () {
5 return Cache::get('key');
6});

在整个 Laravel 文档中,许多示例将使用外观来演示框架的各种功能。

辅助函数

为了补充 Facades,Laravel 提供了各种全局“辅助函数”,让您可以更轻松地与常用的 Laravel 功能进行交互。您可以使用的一些常用辅助函数包括view、、、等等response。Laravel提供的每个辅助函数urlconfig记录了其对应的功能;完整的列表可在专用的辅助函数文档中找到。

例如,Illuminate\Support\Facades\Response我们可以不使用 Facade 来生成 JSON 响应,而是直接使用response函数。由于辅助函数是全局可用的,因此无需导入任何类即可使用它们:

1use Illuminate\Support\Facades\Response;
2 
3Route::get('/users', function () {
4 return Response::json([
5 // ...
6 ]);
7});
8 
9Route::get('/users', function () {
10 return response()->json([
11 // ...
12 ]);
13});

何时使用 Facade

Facades 有很多好处。它们提供简洁易记的语法,让您能够轻松使用 Laravel 的功能,而无需记住必须手动注入或配置的长类名。此外,由于其对 PHP 动态方法的独特使用,它们易于测试。

然而,使用 Facade 时必须谨慎。Facade 的主要风险在于类的“范围蔓延”。由于 Facade 非常易于使用且无需注入,因此很容易导致类不断增长,并在一个类中使用多个 Facade。使用依赖注入,大型构造函数会提供视觉反馈,提醒您类正在变得过大,从而减轻这种风险。因此,使用 Facade 时,请特别注意类的大小,以使其职责范围保持较小。如果类变得过大,请考虑将其拆分为多个较小的类。

外观与依赖注入

依赖注入的主要优点之一是能够交换注入类的实现。这在测试过程中非常有用,因为您可以注入模拟或存根,并断言存根上调用了各种方法。

通常情况下,我们无法模拟或存根一个真正的静态类方法。然而,由于 Facade 使用动态方法将方法调用代理到从服务容器解析的对象上,因此我们实际上可以像测试注入的类实例一样测试 Facade。例如,假设有以下路由:

1use Illuminate\Support\Facades\Cache;
2 
3Route::get('/cache', function () {
4 return Cache::get('key');
5});

使用 Laravel 的外观测试方法,我们可以编写以下测试来验证该Cache::get方法是否使用我们期望的参数调用:

1use Illuminate\Support\Facades\Cache;
2 
3test('basic example', function () {
4 Cache::shouldReceive('get')
5 ->with('key')
6 ->andReturn('value');
7 
8 $response = $this->get('/cache');
9 
10 $response->assertSee('value');
11});
1use Illuminate\Support\Facades\Cache;
2 
3/**
4 * A basic functional test example.
5 */
6public function test_basic_example(): void
7{
8 Cache::shouldReceive('get')
9 ->with('key')
10 ->andReturn('value');
11 
12 $response = $this->get('/cache');
13 
14 $response->assertSee('value');
15}

Facades 与辅助函数

除了 Facades 之外,Laravel 还包含各种“辅助”函数,它们可以执行常见任务,例如生成视图、触发事件、调度作业或发送 HTTP 响应。许多辅助函数的功能与相应的 Facade 相同。例如,以下 Facade 调用和辅助函数调用是等效的:

1return Illuminate\Support\Facades\View::make('profile');
2 
3return view('profile');

Facade 和辅助函数之间没有任何实际区别。使用辅助函数时,你仍然可以像测试相应的 Facade 一样测试它们。例如,给定以下路由:

1Route::get('/cache', function () {
2 return cache('key');
3});

辅助函数cache会调用外观类get底层的类的方法Cache。因此,即使我们使用的是辅助函数,我们也可以编写以下测试来验证该方法是否被调用,并且参数是否符合我们的预期:

1use Illuminate\Support\Facades\Cache;
2 
3/**
4 * A basic functional test example.
5 */
6public function test_basic_example(): void
7{
8 Cache::shouldReceive('get')
9 ->with('key')
10 ->andReturn('value');
11 
12 $response = $this->get('/cache');
13 
14 $response->assertSee('value');
15}

Facade 的工作原理

在 Laravel 应用程序中,Facade 是一个提供对容器中对象的访问的类。实现此功能的机制就在这个Facade类中。Laravel 的 Facade 以及您创建的任何自定义 Facade 都将扩展基Illuminate\Support\Facades\Facade类。

Facade类使用__callStatic()魔法方法将外观组件的调用延迟到从容器解析的对象上。下面的示例中,调用了 Laravel 缓存系统。浏览这段代码,你可能会认为get调用的是Cache类上的静态方法:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use Illuminate\Support\Facades\Cache;
6use Illuminate\View\View;
7 
8class UserController extends Controller
9{
10 /**
11 * Show the profile for the given user.
12 */
13 public function showProfile(string $id): View
14 {
15 $user = Cache::get('user:'.$id);
16 
17 return view('profile', ['user' => $user]);
18 }
19}

请注意,在文件顶部附近,我们“导入”了CacheFacade。该 Facade 充当访问接口底层实现的代理Illuminate\Contracts\Cache\Factory。我们使用该 Facade 进行的任何调用都将传递给 Laravel 缓存服务的底层实例。

如果我们看一下该类Illuminate\Support\Facades\Cache,您会发现它没有静态方法get

1class Cache extends Facade
2{
3 /**
4 * Get the registered name of the component.
5 */
6 protected static function getFacadeAccessor(): string
7 {
8 return 'cache';
9 }
10}

相反,Cache外观类扩展了基Facade类并定义了方法getFacadeAccessor()。此方法的作用是返回服务容器绑定的名称。当用户引用Cache外观类上的任何静态方法时,Laravel 会cache服务容器中解析绑定,并针对该对象运行所请求的方法(在本例中为get)。

实时外观

使用实时 Facade,您可以将应用程序中的任何类视为 Facade。为了说明如何使用它,我们首先来看一下一些不使用实时 Facade 的代码。例如,假设我们的Podcast模型有一个publish方法。但是,为了发布播客,我们需要注入一个Publisher实例:

1<?php
2 
3namespace App\Models;
4 
5use App\Contracts\Publisher;
6use Illuminate\Database\Eloquent\Model;
7 
8class Podcast extends Model
9{
10 /**
11 * Publish the podcast.
12 */
13 public function publish(Publisher $publisher): void
14 {
15 $this->update(['publishing' => now()]);
16 
17 $publisher->publish($this);
18 }
19}

将发布者实现注入到方法中,使我们能够轻松地单独测试该方法,因为我们可以模拟注入的发布者。但是,这要求我们每次调用该publish方法时都必须传递一个发布者实例。使用实时外观,我们可以保持相同的可测试性,同时无需显式传递Publisher实例。要生成实时外观,请在导入类的命名空间中添加前缀Facades

1<?php
2 
3namespace App\Models;
4 
5use App\Contracts\Publisher;
6use Facades\App\Contracts\Publisher;
7use Illuminate\Database\Eloquent\Model;
8 
9class Podcast extends Model
10{
11 /**
12 * Publish the podcast.
13 */
14 public function publish(Publisher $publisher): void
15 public function publish(): void
16 {
17 $this->update(['publishing' => now()]);
18 
19 $publisher->publish($this);
20 Publisher::publish($this);
21 }
22}

当使用实时外观时,发布者实现将使用Facades前缀后面的接口或类名部分从服务容器中解析出来。测试时,我们可以使用 Laravel 内置的外观测试Helpers来模拟此方法调用:

1<?php
2 
3use App\Models\Podcast;
4use Facades\App\Contracts\Publisher;
5use Illuminate\Foundation\Testing\RefreshDatabase;
6 
7uses(RefreshDatabase::class);
8 
9test('podcast can be published', function () {
10 $podcast = Podcast::factory()->create();
11 
12 Publisher::shouldReceive('publish')->once()->with($podcast);
13 
14 $podcast->publish();
15});
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Models\Podcast;
6use Facades\App\Contracts\Publisher;
7use Illuminate\Foundation\Testing\RefreshDatabase;
8use Tests\TestCase;
9 
10class PodcastTest extends TestCase
11{
12 use RefreshDatabase;
13 
14 /**
15 * A test example.
16 */
17 public function test_podcast_can_be_published(): void
18 {
19 $podcast = Podcast::factory()->create();
20 
21 Publisher::shouldReceive('publish')->once()->with($podcast);
22 
23 $podcast->publish();
24 }
25}

Facade 类参考

您将在下方找到每个外观及其底层类。这是一个实用的工具,可帮助您快速深入了解给定外观根的 API 文档。在适用的情况下,还会包含服务容器绑定键。

正面 班级 服务容器绑定
应用程序 照亮\基础\应用 app
工匠 照亮\Contracts\控制台\内核 artisan
授权(实例) 照亮\Contracts\授权\守卫 auth.driver
授权 Illuminate\Auth\AuthManager auth
刀刃 Illuminate\View\Compilers\BladeCompiler blade.compiler
广播(实例) 照亮\Contracts\广播\广播员  
播送 照亮\Contracts\广播\Factories  
公共汽车 照亮\Contracts\巴士\调度员  
缓存(实例) 照亮\缓存\存储库 cache.store
缓存 Illuminate\Cache\CacheManager cache
配置 Illuminate\Config\Repository config
Context 照亮\日志\上下文\存储库  
曲奇饼 照亮\Cookie\CookieJar cookie
墓穴 照亮\加密\加密器 encrypter
日期 Illuminate\Support\DateFactory date
数据库(实例) 照亮\数据库\连接 db.connection
数据库 Illuminate\Database\DatabaseManager db
事件 Illuminate\Events\Dispatcher events
异常(实例) 照亮\Contracts\调试\异常处理程序  
例外 Illuminate\Foundation\Exceptions\Handler  
文件 Illuminate\文件系统\文件系统 files
照亮\Contracts\授权\访问\门  
哈希 Illuminate\Contracts\Hashing\Hasher hash
Http 照亮\Http\客户端\Factories  
照亮\翻译\翻译器 translator
日志 Illuminate\Log\LogManager log
邮件 Illuminate\Mail\Mailer mailer
通知 Illuminate\Notifications\ChannelManager  
密码(实例) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
密码 Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Pipeline(实例) 照亮\Pipeline\Pipeline  
过程 照明\工艺\Factories  
队列(基类) 照亮\队列\队列  
队列(实例) 照亮\Contracts\队列\队列 queue.connection
队列 Illuminate\Queue\QueueManager queue
限流器 Illuminate\Cache\RateLimiter  
重定向 照亮\路由\重定向器 redirect
Redis(实例) Illuminate\Redis\Connections\连接 redis.connection
Redis Illuminate\Redis\RedisManager redis
要求 照亮\Http\请求 request
响应(实例) 照亮\Http\响应  
回复 照亮\Contracts\路由\响应Factories  
路线 照亮\路由\路由器 router
日程 照亮\控制台\调度\调度  
架构 Illuminate\Database\Schema\Builder  
会话(实例) 照亮\会话\商店 session.store
Sessions Illuminate\Session\SessionManager session
存储(实例) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
贮存 Illuminate\Filesystem\FilesystemManager filesystem
网址 照亮\路由\UrlGenerator url
验证器(实例) Illuminate\Validation\Validator  
验证器 照亮\验证\Factories validator
视图(实例) 照亮\查看\查看  
看法 照亮\查看\Factories view
维特 Illuminate\Foundation\Vite