ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
* * * * * [TOC] ## 简介 Facades(读音:/fəˈsäd/ )为应用程序的 [服务容器](https://www.kancloud.cn/tonyyu/laravel_5_6/786056) 中可用的类提供了一个「静态」接口。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的时候,要特别注意控制类的大小,让类的作用范围保持短小。 > {tip} 在开发与 Laravel 进行交互的第三方扩展包时, 建立最好选择注入 [Laravel 契约](https://www.kancloud.cn/tonyyu/laravel_5_6/786059) ,而不是使用 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; /** * 一个基础功能的测试用例。 * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $this->visit('/cache') ->see('value'); } ~~~ ### Facades Vs. 辅助函数 除了 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()` 魔术方法,直到对象从容器中被解析出来后,才会进行调用。在下面的例子中,调用了 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 会从 [服务容器](https://www.kancloud.cn/tonyyu/laravel_5_6/786056) 中解析 `cache` 绑定以及该对象运行所请求的方法(在这个例子中就是 `get` 方法)。 ## 实时 Facades 使用实时 Facades,你可以将应用程序中的任何类视为 Facade。为了说明这是如何使用的,我们来看看另一种方法。例如,假设我们的 `Podcast` 模型有一个 `publish` 方法。然而,为了发布 Podcast,我们需要注入一个 `Publisher`实例: ~~~ <?php namespace App; 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` 实例。要生成实时外观,请在导入类的名称空间中加上 `Facades`: ~~~ <?php namespace App; 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\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(); } } ~~~ ## Facade 类参考 在下面你可以找到每个 Facade 类及其对应的底层类。这是一个查找给定 Facade 类 API 文档的工具。[服务容器绑定](https://www.kancloud.cn/tonyyu/laravel_5_6/786056) 的可用键值也包含在内。 | Facade | 类 | 服务容器绑定 | | --- | --- | --- | | App | [Illuminate\Foundation\Application](https://laravel.com/api/laravel/5.6/Illuminate/Foundation/Application.html) | `app` | | Artisan | [Illuminate\Contracts\Console\Kernel](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Console/Kernel.html) | `artisan` | | Auth | [Illuminate\Auth\AuthManager](https://laravel.com/api/laravel/5.6/Illuminate/Auth/AuthManager.html) | `auth` | | Auth (Instance) | [Illuminate\Contracts\Auth\Guard](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Auth/Guard.html) | `auth.driver` | | Blade | [Illuminate\View\Compilers\BladeCompiler](https://laravel.com/api/laravel/5.6/Illuminate/View/Compilers/BladeCompiler.html) | `blade.compiler` | | Broadcast | [Illuminate\Contracts\Broadcasting\Factory](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Broadcasting/Factory.html) |   | | Broadcast (Instance) | [Illuminate\Contracts\Broadcasting\Broadcaster](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Broadcasting/Broadcaster.html) |   | | Bus | [Illuminate\Contracts\Bus\Dispatcher](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Bus/Dispatcher.html) |   | | Cache | [Illuminate\Cache\CacheManager](https://laravel.com/api/laravel/5.6/Illuminate/Cache/CacheManager.html) | `cache` | | Cache (Instance) | [Illuminate\Cache\Repository](https://laravel.com/api/laravel/5.6/Illuminate/Cache/Repository.html) | `cache.store` | | Config | [Illuminate\Config\Repository](https://laravel.com/api/laravel/5.6/Illuminate/Config/Repository.html) | `config` | | Cookie | [Illuminate\Cookie\CookieJar](https://laravel.com/api/laravel/5.6/Illuminate/Cookie/CookieJar.html) | `cookie` | | Crypt | [Illuminate\Encryption\Encrypter](https://laravel.com/api/laravel/5.6/Illuminate/Encryption/Encrypter.html) | `encrypter` | | DB | [Illuminate\Database\DatabaseManager](https://laravel.com/api/laravel/5.6/Illuminate/Database/DatabaseManager.html) | `db` | | DB (Instance) | [Illuminate\Database\Connection](https://laravel.com/api/laravel/5.6/Illuminate/Database/Connection.html) | `db.connection` | | Event | [Illuminate\Events\Dispatcher](https://laravel.com/api/laravel/5.6/Illuminate/Events/Dispatcher.html) | `events` | | File | [Illuminate\Filesystem\Filesystem](https://laravel.com/api/laravel/5.6/Illuminate/Filesystem/Filesystem.html) | `files` | | Gate | [Illuminate\Contracts\Auth\Access\Gate](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Auth/Access/Gate.html) |   | | Hash | [Illuminate\Contracts\Hashing\Hasher](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Hashing/Hasher.html) | `hash` | | Lang | [Illuminate\Translation\Translator](https://laravel.com/api/laravel/5.6/Illuminate/Translation/Translator.html) | `translator` | | Log | [Illuminate\Log\Logger](https://laravel.com/api/laravel/5.6/Illuminate/Log/Logger.html) | `log` | | Mail | [Illuminate\Mail\Mailer](https://laravel.com/api/laravel/5.6/Illuminate/Mail/Mailer.html) | `mailer` | | Notification | [Illuminate\Notifications\ChannelManager](https://laravel.com/api/laravel/5.6/Illuminate/Notifications/ChannelManager.html) |   | | Password | [Illuminate\Auth\Passwords\PasswordBrokerManager](https://laravel.com/api/laravel/5.6/Illuminate/Auth/Passwords/PasswordBrokerManager.html) | `auth.password` | | Password (Instance) | [Illuminate\Auth\Passwords\PasswordBroker](https://laravel.com/api/laravel/5.6/Illuminate/Auth/Passwords/PasswordBroker.html) | `auth.password.broker` | | Queue | [Illuminate\Queue\QueueManager](https://laravel.com/api/laravel/5.6/Illuminate/Queue/QueueManager.html) | `queue` | | Queue (Instance) | [Illuminate\Contracts\Queue\Queue](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Queue/Queue.html) | `queue.connection` | | Queue (Base Class) | [Illuminate\Queue\Queue](https://laravel.com/api/laravel/5.6/Illuminate/Queue/Queue.html) |   | | Redirect | [Illuminate\Routing\Redirector](https://laravel.com/api/laravel/5.6/Illuminate/Routing/Redirector.html) | `redirect` | | Redis | [Illuminate\Redis\RedisManager](https://laravel.com/api/laravel/5.6/Illuminate/Redis/RedisManager.html) | `redis` | | Redis (Instance) | [Illuminate\Redis\Connections\Connection](https://laravel.com/api/laravel/5.6/Illuminate/Redis/Connections/Connection.html) | `redis.connection` | | Request | [Illuminate\Http\Request](https://laravel.com/api/laravel/5.6/Illuminate/Http/Request.html) | `request` | | Response | [Illuminate\Contracts\Routing\ResponseFactory](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Routing/ResponseFactory.html) |   | | Response (Instance) | [Illuminate\Http\Response](https://laravel.com/api/laravel/5.6/Illuminate/Http/Response.html) |   | | Route | [Illuminate\Routing\Router](https://laravel.com/api/laravel/5.6/Illuminate/Routing/Router.html) | `router` | | Schema | [Illuminate\Database\Schema\Builder](https://laravel.com/api/laravel/5.6/Illuminate/Database/Schema/Builder.html) |   | | Session | [Illuminate\Session\SessionManager](https://laravel.com/api/laravel/5.6/Illuminate/Session/SessionManager.html) | `session` | | Session (Instance) | [Illuminate\Session\Store](https://laravel.com/api/laravel/5.6/Illuminate/Session/Store.html) | `session.store` | | Storage | [Illuminate\Filesystem\FilesystemManager](https://laravel.com/api/laravel/5.6/Illuminate/Filesystem/FilesystemManager.html) | `filesystem` | | Storage (Instance) | [Illuminate\Contracts\Filesystem\Filesystem](https://laravel.com/api/laravel/5.6/Illuminate/Contracts/Filesystem/Filesystem.html) | `filesystem.disk` | | URL | [Illuminate\Routing\UrlGenerator](https://laravel.com/api/laravel/5.6/Illuminate/Routing/UrlGenerator.html) | `url` | | Validator | [Illuminate\Validation\Factory](https://laravel.com/api/laravel/5.6/Illuminate/Validation/Factory.html) | `validator` | | Validator (Instance) | [Illuminate\Validation\Validator](https://laravel.com/api/laravel/5.6/Illuminate/Validation/Validator.html) |   | | View | [Illuminate\View\Factory](https://laravel.com/api/laravel/5.6/Illuminate/View/Factory.html) | `view` | | View (Instance) | [Illuminate\View\View](https://laravel.com/api/laravel/5.6/Illuminate/View/View.html) | |