Facades
介绍
在 Laravel 文档中,您会看到许多通过 "facades" 与 Laravel 功能交互的代码示例。Facades 为应用程序的服务容器中可用的类提供了一个“静态”接口。Laravel 附带了许多 facades,几乎可以访问 Laravel 的所有功能。
Laravel facades 作为服务容器中底层类的“静态代理”,提供了简洁、富有表现力的语法,同时比传统的静态方法具有更高的可测试性和灵活性。如果您不完全理解 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 来演示框架的各种功能。
辅助函数
为了补充 facades,Laravel 提供了多种全局“辅助函数”,使与常见 Laravel 功能的交互更加容易。您可能会使用的一些常见辅助函数有 view
、response
、url
、config
等。Laravel 提供的每个辅助函数都与其对应的功能一起记录;不过,完整列表可在专用的辅助文档中找到。
例如,代替使用 Illuminate\Support\Facades\Response
facade 来生成 JSON 响应,我们可以简单地使用 response
函数。因为辅助函数是全局可用的,您不需要导入任何类即可使用它们:
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 的主要危险是类的“范围蔓延”。由于 facades 使用起来非常简单且不需要注入,因此很容易让您的类继续增长,并在单个类中使用许多 facades。使用依赖注入,这种潜力通过大型构造函数提供的视觉反馈得以缓解,提醒您类的规模过大。因此,使用 facades 时,请特别注意类的大小,以确保其责任范围保持狭窄。如果您的类变得过大,请考虑将其拆分为多个较小的类。
Facades 与依赖注入
依赖注入的主要好处之一是能够交换注入类的实现。这在测试期间非常有用,因为您可以注入一个模拟或存根,并断言在存根上调用了各种方法。
通常,不可能模拟或存根真正的静态类方法。然而,由于 facades 使用动态方法将方法调用代理到从服务容器解析的对象,我们实际上可以像测试注入的类实例一样测试 facades。例如,给定以下路由:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
使用 Laravel 的 facade 测试方法,我们可以编写以下测试来验证 Cache::get
方法是否使用我们预期的参数被调用:
use Illuminate\Support\Facades\Cache;
/**
* 基本功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
Facades 与辅助函数
除了 facades,Laravel 还包括多种“辅助”函数,可以执行常见任务,如生成视图、触发事件、调度作业或发送 HTTP 响应。许多这些辅助函数执行与相应 facade 相同的功能。例如,这个 facade 调用和辅助调用是等价的:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
在 facades 和辅助函数之间没有实际区别。使用辅助函数时,您仍然可以像测试相应的 facade 一样测试它们。例如,给定以下路由:
Route::get('/cache', function () {
return cache('key');
});
在底层,cache
辅助函数将调用 Cache
facade 底层类的 get
方法。因此,即使我们使用辅助函数,我们也可以编写以下测试来验证该方法是否使用我们预期的参数被调用:
use Illuminate\Support\Facades\Cache;
/**
* 基本功能测试示例。
*
* @return void
*/
public function testBasicExample()
{
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 缓存系统。通过查看此代码,您可能会认为静态 get
方法正在 Cache
类上被调用:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* 显示给定用户的个人资料。
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
请注意,在文件的顶部,我们正在“导入” Cache
facade。这个 facade 作为访问 Illuminate\Contracts\Cache\Factory
接口底层实现的代理。我们使用 facade 进行的任何调用都将传递给 Laravel 缓存服务的底层实例。
如果我们查看 Illuminate\Support\Facades\Cache
类,您会看到没有静态方法 get
:
class Cache extends Facade
{
/**
* 获取组件的注册名称。
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
相反,Cache
facade 扩展了基本的 Facade
类,并定义了方法 getFacadeAccessor()
。此方法的工作是返回服务容器绑定的名称。当用户引用 Cache
facade 上的任何静态方法时,Laravel 将从服务容器解析 cache
绑定,并针对该对象运行请求的方法(在本例中为 get
)。
实时 Facades
使用实时 facades,您可以将应用程序中的任何类视为 facade。为了说明如何使用它,让我们首先检查一些不使用实时 facades 的代码。例如,假设我们的 Podcast
模型有一个 publish
方法。然而,为了发布播客,我们需要注入一个 Publisher
实例:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* 发布播客。
*
* @param Publisher $publisher
* @return void
*/
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
将发布者实现注入方法中允许我们轻松地单独测试该方法,因为我们可以模拟注入的发布者。然而,这要求我们每次调用 publish
方法时都显式传递一个发布者实例。使用实时 facades,我们可以保持相同的可测试性,而不需要显式传递 Publisher
实例。要生成实时 facade,请在导入类的命名空间前加上 Facades
前缀:
<?php
namespace App\Models;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* 发布播客。
*
* @return void
*/
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
使用实时 facade 时,发布者实现将使用 Facades
前缀后出现的接口或类名部分从服务容器中解析出来。在测试时,我们可以使用 Laravel 内置的 facade 测试助手来模拟此方法调用:
<?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;
/**
* 测试示例。
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
Facade 类参考
下面您将找到每个 facade 及其底层类。这是快速深入了解给定 facade 根的 API 文档的有用工具。还包括适用的服务容器绑定键。
Facade | 类 | 服务容器绑定 |
---|---|---|
App | [Illuminate\Foundation\Application](https://laravel.com/api/8.x/Illuminate/Foundation/Application.html) | `app` |
Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/8.x/Illuminate/Contracts/Console/Kernel.html) | `artisan` |
Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/8.x/Illuminate/Auth/AuthManager.html) | `auth` |
Auth (实例) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/8.x/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` |
Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/8.x/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` |
Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/8.x/Illuminate/Contracts/Broadcasting/Factory.html) | |
Broadcast (实例) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/8.x/Illuminate/Contracts/Broadcasting/Broadcaster.html) | |
Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/8.x/Illuminate/Contracts/Bus/Dispatcher.html) | |
Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/8.x/Illuminate/Cache/CacheManager.html) | `cache` |
Cache (实例) | [Illuminate\Cache\Repository](https://laravel.com/api/8.x/Illuminate/Cache/Repository.html) | `cache.store` |
Config | [Illuminate\Config\Repository](https://laravel.com/api/8.x/Illuminate/Config/Repository.html) | `config` |
Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/8.x/Illuminate/Cookie/CookieJar.html) | `cookie` |
Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/8.x/Illuminate/Encryption/Encrypter.html) | `encrypter` |
Date | [Illuminate\Support\DateFactory](https://laravel.com/api/8.x/Illuminate/Support/DateFactory.html) | `date` |
DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/8.x/Illuminate/Database/DatabaseManager.html) | `db` |
DB (实例) | [Illuminate\Database\Connection](https://laravel.com/api/8.x/Illuminate/Database/Connection.html) | `db.connection` |
Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/8.x/Illuminate/Events/Dispatcher.html) | `events` |
File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/8.x/Illuminate/Filesystem/Filesystem.html) | `files` |
Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/8.x/Illuminate/Contracts/Auth/Access/Gate.html) | |
Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/8.x/Illuminate/Contracts/Hashing/Hasher.html) | `hash` |
Http | [Illuminate\Http\Client\Factory](https://laravel.com/api/8.x/Illuminate/Http/Client/Factory.html) | |
Lang | [Illuminate\Translation\Translator](https://laravel.com/api/8.x/Illuminate/Translation/Translator.html) | `translator` |
Log | [Illuminate\Log\LogManager](https://laravel.com/api/8.x/Illuminate/Log/LogManager.html) | `log` |
[Illuminate\Mail\Mailer](https://laravel.com/api/8.x/Illuminate/Mail/Mailer.html) | `mailer` | |
Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/8.x/Illuminate/Notifications/ChannelManager.html) | |
Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/8.x/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` |
Password (实例) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/8.x/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` |
Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/8.x/Illuminate/Queue/QueueManager.html) | `queue` |
Queue (实例) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/8.x/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` |
Queue (基类) | [Illuminate\Queue\Queue](https://laravel.com/api/8.x/Illuminate/Queue/Queue.html) | |
Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/8.x/Illuminate/Routing/Redirector.html) | `redirect` |
Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/8.x/Illuminate/Redis/RedisManager.html) | `redis` |
Redis (实例) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/8.x/Illuminate/Redis/Connections/Connection.html) | `redis.connection` |
Request | [Illuminate\Http\Request](https://laravel.com/api/8.x/Illuminate/Http/Request.html) | `request` |
Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/8.x/Illuminate/Contracts/Routing/ResponseFactory.html) | |
Response (实例) | [Illuminate\Http\Response](https://laravel.com/api/8.x/Illuminate/Http/Response.html) | |
Route | [Illuminate\Routing\Router](https://laravel.com/api/8.x/Illuminate/Routing/Router.html) | `router` |
Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/8.x/Illuminate/Database/Schema/Builder.html) | |
Session | [Illuminate\Session\SessionManager](https://laravel.com/api/8.x/Illuminate/Session/SessionManager.html) | `session` |
Session (实例) | [Illuminate\Session\Store](https://laravel.com/api/8.x/Illuminate/Session/Store.html) | `session.store` |
Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/8.x/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` |
Storage (实例) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/8.x/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` |
URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/8.x/Illuminate/Routing/UrlGenerator.html) | `url` |
Validator | [Illuminate\Validation\Factory](https://laravel.com/api/8.x/Illuminate/Validation/Factory.html) | `validator` |
Validator (实例) | [Illuminate\Validation\Validator](https://laravel.com/api/8.x/Illuminate/Validation/Validator.html) | |
View | [Illuminate\View\Factory](https://laravel.com/api/8.x/Illuminate/View/Factory.html) | `view` |
View (实例) | [Illuminate\View\View](https://laravel.com/api/8.x/Illuminate/View/View.html) |