跳到主要内容
版本:5.3

Facades

简介​

Facades /fəˈsäd/ 为应用程序的服务容器中可用的类提供了一个「静态」接口。Laravel 自带了许多的 facades,可以用来访问其几乎所有的服务。Laravel facades 就是服务容器里那些基类的「静态代理」,相比于传统的静态方法调用,facades 在提供更简洁且丰富的语法的同时,还有更好的可测试性和扩展性。

所有的 Laravel facades 都在 Illuminate\Support\Facades 这个命名空间下。所以我们可以用类似下面的代码轻松调用:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
return Cache::get('key');
});

在 Laravel 的文档中,很多代码示例都使用了 facades 来演示框架的各种特性。

何时使用 Facades​

Facades 有很多的好处,它们提供了简单易记的语法让你使用 Laravel 的各种功能,你再也不需要记住长长的类名来实现注入或者手动去配置。还有,因为它们对于 PHP 动态方法的独特用法,测试起来就非常容易。

但是,在使用 facades 时,有些地方还是需要特别注意。使用 facades 最主要的风险就是会引起类的体积的膨胀。由于 facades 使用起来非常简单而且不需要注入,我们会不经意间在单个类中大量使用。不像是使用依赖注入,用得越多,构造方法会越长,在视觉上就会引起注意,提醒你这个类有点太庞大了。所以,在使用 facades 时,要特别注意把类的体积控制在一个合理的范围。

提示

在开发第三方扩展包时,如果需要使用和 Laravel 交互的扩展包,最好是注入 Laravel contracts 而不是使用 facades,因为扩展包不是在 Laravel 内部使用的,无法使用 Laravel 的 facades 的测试辅助函数。

Facades Vs. 依赖注入​

依赖注入一个主要的好处就是可以切换注入的类的具体实现。这在测试时很有用,因为你可以注入一个 mock 或者 stub 方法并且断言一些方法在 stub 中被调用。

在以前,静态方法是不可能被 mock 或者 stub 的。但是因为 facades 调用的是对象的动态方法,我可以像测试注入类的实例一样测试 facades。举个例子,有这样的一个路由:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
return Cache::get('key');
});

我们可以用下面的测试代码去验证 Cache::get 方法是否像我们预期那样被调用并传入参数。

use Illuminate\Support\Facades\Cache;

/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');

$this->visit('/cache')
->see('value');
}

Facades Vs. 辅助函数​

除了 facades,Laravel 还引入了一些「辅助函数」来实现一些常用的功能,比如生成视图、触发事件、调度任务或者发送 HTTP 响应。许多辅助函数的功能其实和对应的 facades 一样。举例来说,这个 facede 和辅助函数是一样的:

return View::make('profile');

return view('profile');

Facade 和辅助函数其实没有本质区别,当使用辅助函数时,你依然可以像使用对应的 facade 一样测试它们。比如,下面的路由:

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

在底层,辅助函数 cache 会调用 Cache facade 的基类的 get 方法。因此,即使我们是在使用辅助函数,我们依然可以用下面的代码来测试方法是不是被正确的调用了:

use Illuminate\Support\Facades\Cache;

/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');

$this->visit('/cache')
->see('value');
}

Facades 工作原理​

在 Laravel 应用中,一个 facade 其实就是一个提供访问容器中对象功能的类。其中最核心的部件就是 Facade 类。不管是 Laravel 自带的,还是用户自定义的 Facades,都是继承了 Illuminate\Support\Facades\Facade 这个类。

Facade 类利用了 __callStatic() 这个魔术方法来延迟调用容器中的对象的方法。在下面的例子里,调用了 Laravel 缓存系统。在代码里,我们可能以为 Cache 这个类的静态方法 get 被调用了:

<?php

namespace App\Http\Controllers;

use Cache;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @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
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}

其实,Cache facade 是继承了基类 Facade 并且定义了 getFacadeAccessor() 方法。这个方法的功能是返回服务容器中绑定的名字。当用户调用 Cache facade 的静态方法时,Laravel 会解析到 cache 在服务容积里绑定的具体的那个实例对象,并且调用这个对象的相应方法(在这个例子里就是 get 方法)。

Facade 类参考​

在下方你可以找到每个 facade 及其底层的类。这个工具对于通过指定 facade 的来源快速寻找 API 文档相当有用。可应用的服务容器绑定关键字也包含在里面。

FacadeClassService Container Binding
AppIlluminate\Foundation\Applicationapp
ArtisanIlluminate\Contracts\Console\Kernelartisan
AuthIlluminate\Auth\AuthManagerauth
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BusIlluminate\Contracts\Bus\Dispatcher
CacheIlluminate\Cache\Repositorycache
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
DB (Instance)Illuminate\Database\Connection
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate
HashIlluminate\Contracts\Hashing\Hasherhash
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\Writerlog
MailIlluminate\Mail\Mailermailer
PasswordIlluminate\Auth\Passwords\PasswordBrokerauth.password
QueueIlluminate\Queue\QueueManagerqueue
Queue (Instance)Illuminate\Contracts\Queue\Queuequeue
Queue (Base Class)Illuminate\Queue\Queue
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\Databaseredis
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory
RouteIlluminate\Routing\Routerrouter
SchemaIlluminate\Database\Schema\Blueprint
SessionIlluminate\Session\SessionManagersession
Session (Instance)Illuminate\Session\Store
StorageIlluminate\Contracts\Filesystem\Factoryfilesystem
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator (Instance)Illuminate\Validation\Validator
ViewIlluminate\View\Factoryview
View (Instance)Illuminate\View\View

备注

欢迎任何形式的转载,但请务必注明出处,尊重他人劳动共创开源社区。

转载请注明:本文档由 Laravel China 社区 [laravel-china.org] 组织翻译,详见 翻译召集帖。

文档永久地址: http://d.laravel-china.org