Skip to content

Facades

介绍

Facades为应用程序的服务容器中可用的类提供了一个“静态”接口。Laravel附带了许多Facades,几乎可以访问Laravel的所有功能。Laravel的Facades作为服务容器中底层类的“静态代理”,提供了简洁、富有表现力的语法,同时比传统的静态方法具有更好的可测试性和灵活性。

所有的Laravel Facades都定义在 Illuminate\Support\Facades 命名空间中。因此,我们可以轻松地访问一个Facade,如下所示:

use Illuminate\Support\Facades\Cache;

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

在Laravel文档中,许多示例将使用Facades来演示框架的各种功能。

何时使用Facades

Facades有很多好处。它们提供了一种简洁、易记的语法,使您可以在不记住长类名的情况下使用Laravel的功能,这些类名必须手动注入或配置。此外,由于它们独特地使用PHP的动态方法,它们易于测试。

然而,使用Facades时需要注意一些事项。Facades的主要危险在于类的范围蔓延。由于Facades使用起来非常简单且不需要注入,因此很容易让您的类继续增长,并在单个类中使用许多Facades。使用依赖注入,这种潜力通过大型构造函数给您的视觉反馈得以缓解,提示您的类变得过大。因此,在使用Facades时,请特别注意类的大小,以确保其责任范围保持狭窄。

在构建与Laravel交互的第三方包时,最好注入Laravel合同而不是使用Facades。由于包是在Laravel本身之外构建的,您将无法访问Laravel的Facade测试助手。

Facades与依赖注入

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

通常,不可能模拟或存根真正的静态类方法。然而,由于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响应。许多这些辅助函数执行与相应Facade相同的功能。例如,这个Facade调用和辅助调用是等价的:

return 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');

    $this->visit('/cache')
         ->see('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。为了说明如何使用它,让我们检查一个替代方案。例如,假设我们的 Podcast 模型有一个 publish 方法。然而,为了发布播客,我们需要注入一个 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 前缀:

<?php

namespace App;

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\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 = factory(Podcast::class)->create();

        Publisher::shouldReceive('publish')->once()->with($podcast);

        $podcast->publish();
    }
}

Facade类参考

下面您将找到每个Facade及其底层类。这是一个快速深入了解给定Facade根的API文档的有用工具。还包括服务容器绑定键(如适用)。

Facade服务容器绑定
App[Illuminate\Foundation\Application](https://laravel.com/api/6.x/Illuminate/Foundation/Application.html) `app`
Artisan[Illuminate\Contracts\Console\Kernel](https://laravel.com/api/6.x/Illuminate/Contracts/Console/Kernel.html) `artisan`
Auth[Illuminate\Auth\AuthManager](https://laravel.com/api/6.x/Illuminate/Auth/AuthManager.html) `auth`
Auth (实例)[Illuminate\Contracts\Auth\Guard](https://laravel.com/api/6.x/Illuminate/Contracts/Auth/Guard.html) `auth.driver`
Blade[Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/6.x/Illuminate/View/Compilers/BladeCompiler.html) `blade.compiler`
Broadcast[Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/6.x/Illuminate/Contracts/Broadcasting/Factory.html) 
Broadcast (实例)[Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/6.x/Illuminate/Contracts/Broadcasting/Broadcaster.html) 
Bus[Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/6.x/Illuminate/Contracts/Bus/Dispatcher.html) 
Cache[Illuminate\Cache\CacheManager](https://laravel.com/api/6.x/Illuminate/Cache/CacheManager.html) `cache`
Cache (实例)[Illuminate\Cache\Repository](https://laravel.com/api/6.x/Illuminate/Cache/Repository.html) `cache.store`
Config[Illuminate\Config\Repository](https://laravel.com/api/6.x/Illuminate/Config/Repository.html) `config`
Cookie[Illuminate\Cookie\CookieJar](https://laravel.com/api/6.x/Illuminate/Cookie/CookieJar.html) `cookie`
Crypt[Illuminate\Encryption\Encrypter](https://laravel.com/api/6.x/Illuminate/Encryption/Encrypter.html) `encrypter`
DB[Illuminate\Database\DatabaseManager](https://laravel.com/api/6.x/Illuminate/Database/DatabaseManager.html) `db`
DB (实例)[Illuminate\Database\Connection](https://laravel.com/api/6.x/Illuminate/Database/Connection.html) `db.connection`
Event[Illuminate\Events\Dispatcher](https://laravel.com/api/6.x/Illuminate/Events/Dispatcher.html) `events`
File[Illuminate\Filesystem\Filesystem](https://laravel.com/api/6.x/Illuminate/Filesystem/Filesystem.html) `files`
Gate[Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/6.x/Illuminate/Contracts/Auth/Access/Gate.html) 
Hash[Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/6.x/Illuminate/Contracts/Hashing/Hasher.html) `hash`
Lang[Illuminate\Translation\Translator](https://laravel.com/api/6.x/Illuminate/Translation/Translator.html) `translator`
Log[Illuminate\Log\LogManager](https://laravel.com/api/6.x/Illuminate/Log/LogManager.html) `log`
Mail[Illuminate\Mail\Mailer](https://laravel.com/api/6.x/Illuminate/Mail/Mailer.html) `mailer`
Notification[Illuminate\Notifications\ChannelManager](https://laravel.com/api/6.x/Illuminate/Notifications/ChannelManager.html) 
Password[Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/6.x/Illuminate/Auth/Passwords/PasswordBrokerManager.html) `auth.password`
Password (实例)[Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/6.x/Illuminate/Auth/Passwords/PasswordBroker.html) `auth.password.broker`
Queue[Illuminate\Queue\QueueManager](https://laravel.com/api/6.x/Illuminate/Queue/QueueManager.html) `queue`
Queue (实例)[Illuminate\Contracts\Queue\Queue](https://laravel.com/api/6.x/Illuminate/Contracts/Queue/Queue.html) `queue.connection`
Queue (基类)[Illuminate\Queue\Queue](https://laravel.com/api/6.x/Illuminate/Queue/Queue.html) 
Redirect[Illuminate\Routing\Redirector](https://laravel.com/api/6.x/Illuminate/Routing/Redirector.html) `redirect`
Redis[Illuminate\Redis\RedisManager](https://laravel.com/api/6.x/Illuminate/Redis/RedisManager.html) `redis`
Redis (实例)[Illuminate\Redis\Connections\Connection](https://laravel.com/api/6.x/Illuminate/Redis/Connections/Connection.html) `redis.connection`
Request[Illuminate\Http\Request](https://laravel.com/api/6.x/Illuminate/Http/Request.html) `request`
Response[Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/6.x/Illuminate/Contracts/Routing/ResponseFactory.html) 
Response (实例)[Illuminate\Http\Response](https://laravel.com/api/6.x/Illuminate/Http/Response.html) 
Route[Illuminate\Routing\Router](https://laravel.com/api/6.x/Illuminate/Routing/Router.html) `router`
Schema[Illuminate\Database\Schema\Builder](https://laravel.com/api/6.x/Illuminate/Database/Schema/Builder.html) 
Session[Illuminate\Session\SessionManager](https://laravel.com/api/6.x/Illuminate/Session/SessionManager.html) `session`
Session (实例)[Illuminate\Session\Store](https://laravel.com/api/6.x/Illuminate/Session/Store.html) `session.store`
Storage[Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/6.x/Illuminate/Filesystem/FilesystemManager.html) `filesystem`
Storage (实例)[Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/6.x/Illuminate/Contracts/Filesystem/Filesystem.html) `filesystem.disk`
URL[Illuminate\Routing\UrlGenerator](https://laravel.com/api/6.x/Illuminate/Routing/UrlGenerator.html) `url`
Validator[Illuminate\Validation\Factory](https://laravel.com/api/6.x/Illuminate/Validation/Factory.html) `validator`
Validator (实例)[Illuminate\Validation\Validator](https://laravel.com/api/6.x/Illuminate/Validation/Validator.html) 
View[Illuminate\View\Factory](https://laravel.com/api/6.x/Illuminate/View/Factory.html) `view`
View (实例)[Illuminate\View\View](https://laravel.com/api/6.x/Illuminate/View/View.html)