Facades

    所有的 Laravel Facades 都在 命名空间中定义。所以,我们可以轻松地使用 Facade :

    在 Laravel 的文档中,很多示例代码都会使用 Facades 来演示框架的各种功能。

    Facades 有很多好处, 它为我们使用 Laravel 的功能提供了简单、易记的语法,而无需记住必须手动注入或配置的长长的类名。此外,由于他们对 PHP 动态方法的独特用法,使得测试起来非常容易。

    然而,在使用 Facades 时,有些地方需要特别注意。使用 Facades 最主要的风险就是会引起类作用范围的膨胀。因为 Facades 使用起来非常简单且不需要注入,就会使得我们不经意间在单个类中使用许多 Facades,从而导致类变的越来越大。而使用依赖注入的时候,使用的类越多,构造方法就会越长,在视觉上就会引起注意,提醒你这个类有些庞大了。 因此在使用 Facades的时候,要特别注意控制类的大小,让类的作用范围保持短小。

    依赖注入的主要优点之一是切换注入类的实现的能力。 这在测试的时候很有用,因为你可以注入一个 mock 或者 stub ,并断言在 stub 上调用的各种方法。

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

    我们可以用下面的测试代码来验证带预期参数的 Cache::get 方法

    1. use Illuminate\Support\Facades\Cache;
    2. /**
    3. * 一个基础功能的测试用例。
    4. *
    5. * @return void
    6. */
    7. public function testBasicExample()
    8. {
    9. Cache::shouldReceive('get')
    10. ->with('key')
    11. ->andReturn('value');
    12. $this->visit('/cache')
    13. ->see('value');
    14. }

    Facades Vs. 辅助函数

    除了 Facades, Laravel 还包含各种 『辅助函数』来实现一些常用功能,比如生成视图、触发事件、任务调度或者发送 HTTP 响应。 许多辅助函数都有与之对应的 Facade 。 例如,下面这个 Facade 和辅助函数的作用是一样的。

    这里的 Facades 和辅助函数之间没有实际的区别。当你使用辅助函数时,你可以使用对应的 Facade 进行测试。例如,下面的路由:

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

    在底层实现,辅助函数 cache 实际是调用 Cache 这个 facade 的 get 方法。因此,尽管我们使用的是辅助函数,我们依然可以编写以下测试来验证该方法是否正常调用:

    1. use Illuminate\Support\Facades\Cache;
    2. /**
    3. *
    4. * @return void
    5. */
    6. public function testBasicExample()
    7. {
    8. Cache::shouldReceive('get')
    9. ->with('key')
    10. ->andReturn('value');
    11. $this->visit('/cache')
    12. ->see('value');
    13. }

    在 Laravel 应用中, Facade 就是一个可以从容器访问对象的类。其中核心的部件就是 Facade 类。 不管是 Laravel 自带的 Facades , 还是自定义的 Facades ,都继承自 Illuminate\Support\Facades\Facade 类。

    Facade 基类使用了 __callStatic() 魔术方法,直到对象从容器中被解析出来后,才会进行调用。在下面的例子中,调用了 Laravel 的缓存系统。通过浏览这段代码,可以假定在 Cache类中调用了静态方法 get

    如果我们看一下 这个类,你会发现类中根本没有 get 这个静态方法:

    1. class Cache extends Facade
    2. {
    3. /**
    4. * 获取组件的注册名称。
    5. *
    6. * @return string
    7. */
    8. protected static function getFacadeAccessor() { return 'cache'; }
    9. }

    Cache Facade 继承了Facade 类,并且定义了 getFacadeAccessor() 方法。这个方法的作用是返回服务容器绑定的名称。当用户调用 Cache Facade 中的任何静态方法时,Laravel 会从 中解析 cache 绑定以及该对象运行所请求的方法(在这个例子中就是 get 方法)。

    使用实时 Facades,你可以将应用程序中的任何类视为 Facade。为了说明这是如何使用的,我们来看看另一种方法。例如,假设我们的 Podcast 模型有一个 publish 方法。然而,为了发布 Podcast,我们需要注入一个 Publisher 实例:

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

    将发布者的实现注入到该方法中,我们可以轻松地测试这种方法,因为我们可以模拟注入的发布者。但是,它要求我们每次调用 publish 方法时都要传递一个发布者实例。使用实时的 Facades,我们可以保持同样的可测试性,而不需要显式地通过 Publisher 实例。要生成实时外观,请在导入类的名称空间中加上 Facades

    当使用实时 Facade 时,发布者实现将通过使用 Facades 前缀后出现的接口或类名的部分来解决服务容器的问题。在测试时,我们可以使用 Laravel 的内置 facade 测试辅助函数来模拟这种方法调用:

    1. <?php
    2. namespace Tests\Feature;
    3. use App\Podcast;
    4. use Tests\TestCase;
    5. use Facades\App\Contracts\Publisher;
    6. class PodcastTest extends TestCase
    7. {
    8. use RefreshDatabase;
    9. /**
    10. * 一个测试演示。
    11. *
    12. * @return void
    13. */
    14. public function test_podcast_can_be_published()
    15. {
    16. $podcast = factory(Podcast::class)->create();
    17. Publisher::shouldReceive('publish')->once()->with($podcast);
    18. $podcast->publish();
    19. }

    在下面你可以找到每个 Facade 类及其对应的底层类。这是一个查找给定 Facade 类 API 文档的工具。服务容器绑定 的可用键值也包含在内。

    本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
    我们的翻译工作遵照 ,如果我们的工作有侵犯到您的权益,请及时联系我们。