Facades

未匹配的标注

Facades

简介

在整个 Laravel 文档中,您将看到通过 Facades 与 Laravel 特性交互的代码示例。Facades 为应用程序的 服务容器 中可用的类提供了 『静态代理』。在 Laravel 这艘船上有许多 Facades,提供了几乎所有 Laravel 的特征。

Laravel Facades 充当服务容器中底层类的 『静态代理』,提供简洁、富有表现力的好处,同时保持比传统静态方法更多的可测试性和灵活性。如果你不完全理解引擎盖下的 Facades 是如何工作的,那也没问题,跟着流程走,继续学习 Laravel。

Laravel的所有 Facades 都在 Illuminate\Support\facades 命名空间中定义。因此,我们可以很容易地访问这样一个 Facades:

use Illuminate\Support\Facades\Cache;

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

在整个 Laravel 文档中,许多示例将使用 Facades 来演示框架的各种特性。

助手函数

为了补充 Facades,Laravel 提供了各种全局 「助手函数」,使它更容易与常见的 Laravel 功能进行交互。可以与之交互的一些常用助手函数有 viewresponseurlconfig 等。Laravel提供的每个助手函数都有相应的特性;但是,在专用的 助手函数文档 中有一个完整的列表。

例如,我们可以简单地使用 Response 函数,而不是使用 Illighte\Support\Facades\Response Facade 来生成 JSON 响应。因为 helper 函数是全局可用的,所以不需要导入任何类就可以使用它们:

use Illuminate\Support\Facades\Response;

Route::get('/users', function () {
    return Response::json([
        // ...
    ]);
});

Route::get('/users', function () {
    return response()->json([
        // ...
    ]);
});

什么时候使用 Facades

Facade 有很多好处。它们提供了一种简洁、令人难忘的语法,允许您使用 Laravel 的特性,而无需记住必须手动注入或配置的长类名。此外,由于它们对 PHP 的动态方法的独特用法,它们很容易测试:

但是,在使用 Facade 时必须小心。Facade 的主要危险是「范围溢出」。由于 Facade 非常容易使用并且不需要注入,所以让类继续增长并在单个类中使用多个 Facade 是很容易的。通过使用依赖注入,一个大型构造函数给您的视觉反馈减轻了这种可能性,即类增长过大。所以,在使用 Facade 的时候,要特别注意你的类规模,这样它的职责范围就不会太窄。如果你的类太大了,可以考虑把它分成多个较小的类。

Facades Vs. 依赖注入

依赖注入的主要好处之一是能够交换注入类的实现。这在测试期间非常有用,因为您可以注入一个 mock 或 stub 并断言在 stub 上调用了各种方法。

通常,真正的静态方法是不可能 mock 或 stub 的。 但是由于 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。例如,下面这个 Facades 和辅助函数的作用是一样的:

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 自带的 Facade, 还是自定义的 Facade, 都继承自 Illuminate\Support\Facades\Facade 类。

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

<?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

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

<?php

namespace App\Models;

use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;

class Podcast extends Model
{
    /**
     * 发布 Podcast.
     *
     * @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
{
    /**
     * 发布 Podcast.
     *
     * @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 app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory  
Broadcast (Instance) Illuminate\Contracts\Broadcasting\Broadcaster  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\CacheManager cache
Cache (Instance) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Http Illuminate\Http\Client\Factory  
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Password (Instance) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue.connection
Queue (Base Class) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis (Instance) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
Response (Instance) Illuminate\Http\Response  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Builder  
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
Storage (Instance) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (Instance) Illuminate\Validation\Validator  
View Illuminate\View\Factory view
View (Instance) Illuminate\View\View  

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
贡献者:8
讨论数量: 0
发起讨论 只看当前版本


暂无话题~