Facades
Facades 为应用的 提供了一个「静态」 接口。Laravel 自带了很多 Facades,可以访问绝大部分功能。Laravel Facades 实际是服务容器中底层类的 「静态代理」 ,相对于传统静态方法,在使用时能够提供更加灵活、更加易于测试、更加优雅的语法。
所有的 Laravel Facades 都定义在 命名空间下。所以,我们可以轻松的使用 Facade :
在 Laravel 文档中,有很多示例代码都会使用 Facades 来演示框架的各种功能。
Facades 有很多优点,它提供了简单,易记的语法,从而无需手动注入或配置长长的类名。此外,由于他们对 PHP 静态方法的独特调用,使得测试起来非常容易。
然而,在使用 Facades 时,有些地方需要特别注意。使用 Facades 时最主要的危险就是会引起类作用范围的膨胀。由于 Facades 使用起来非常简单并且不需要注入,就会使得我们不经意间在单个类中使用许多 Facades ,从而导致类变得越来越大。然而使用依赖注入的时候,使用的类越多,构造方法就会越长,在视觉上注意到这个类有些庞大了。因此在使用 Facades 的时候,要特别注意控制类的大小,让类的作用范围保持短小。
通常,真正的静态方法是不可能 mock 或 stub 的。但是 Facades 使用动态方法对服务容器中解析出来的对象方法的调用进行了代理,我们也可以像测试注入类实例一样测试 Facades。比如,像下面的路由:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
我们可以带上我们期望的参数编写下面的测试代码来验证 Cache::get
方法:
use Illuminate\Support\Facades\Cache;
/**
* 一个基础功能的测试。
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Facades 相较于辅助函数
除了 Facades,Laravel 还包含各种 『辅助函数』 来实现这些常用功能,比如生成视图、触发事件、任务调度或者发送 HTTP 响应。许多辅助函数都有与之对应的 Facades 。例如,下面这个 Facades 和辅助函数的作用是一样的:
Facade 和辅助函数之间没有实际的区别。当你使用辅助函数时,你可以像测试相应的 Facade 那样进行测试。例如,下面的路由:
Route::get('/cache', function () {
});
在底层实现,辅助函数 cache
实际是调用 Cache
这个 Facade 的 get
方法。因此,尽管我们使用的是辅助函数,我们依然可以带上我们期望的参数编写下面的测试代码来验证该方法:
use Illuminate\Support\Facades\Cache;
/**
* 一个基础功能的测试用例。
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
在 Laravel 应用中,Facade 就是一个可以从容器访问对象的类。其中核心的部件就是 Facade
类。不管是 Laravel 自带的 Facades,还是自定义的 Facades,都继承自 Illuminate\Support\Facades\Facade
类。
注意在上面这段代码中,我们『导入』了 Cache
Facade。这个 Facade 作为访问 Illuminate\Contracts\Cache\Factory
接口底层实现的代理。我们使用 Facade 进行的任何调用都将传递给 Laravel 缓存服务的底层实例。
如果我们看一下 这个类,你会发现类中根本没有 get
这个静态方法:
class Cache extends Facade
{
/**
* 获取组件的注册名称。
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
Cache
Facade 继承了 Facade 类,并且定义了 getFacadeAccessor()
方法。这个方法的作用是返回服务容器绑定的名称。当用户调用 Cache
Facade 中的任何静态方法时,Laravel 会从 中解析 cache
绑定以及该对象运行所请求的方法(在这个例子中就是 get
方法)。
使用实时 Facades,你可以将应用程序中的任何类视为 Facade。为了说明这是如何使用的,我们来看看另一种方法。例如,假设我们的 Podcast
模型有一个publish
方法。然而,为了发布 Podcast,我们需要注入一个 Publisher
实例:
<?php
namespace App;
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:
当使用实时 Facade 时,发布者实现将通过使用 Facades
前缀后出现的接口或类名的部分来解决服务容器的问题。在测试时,我们可以使用 Laravel 的内置 facade 测试辅助函数来模拟这种方法调用:
<?php
namespace Tests\Feature;
use App\Podcast;
use Tests\TestCase;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* 一个测试演示。
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = factory(Podcast::class)->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
本文章首发在 LearnKu.com 网站上。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 ,如果我们的工作有侵犯到您的权益,请及时联系我们。