Laravel 底层原理:门面(Facades)

Laravel 底层原理:门面(Facades)

大家好,又见面了,我是全栈君。

简介

Facades 为应用服务容器中的绑定类提供了一个“静态”接口。

Laravel 内置了很多 Facades ,可以访问绝大部分 Laravel 的功能。

Laravel 的门面作为服务容器中底层类的“静态代理”,相比于传统静态方法,在维护时能够提供更加易于测试、更加灵活、简明优雅的语法。

Laravel 的所有门面都定义在 Illuminate\Support\Facades 命名空间下。

我们可以轻松访问到门面:

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

在整个 Laravel 文档中,很多例子使用了门面来演示框架的各种功能特性。

何时使用 Facades

门面有诸多优点,其提供了简单、易记的语法,让我们无需记住长长的类名即可使用 Laravel 提供的功能特性,此外,由于他们对 PHP 动态方法的独到用法,使得它们很容易测试。

在使用 Facades 时,有些地方还需要特别注意。

使用 Facades 最主要的风险就是会引起类作用范围的膨胀。

因为 Facades 使用起来非常简单而且不需要注入,就会使得我们在不经意间在单个类中使用许多 Facades,从而导致类变的越来越大。

而使用依赖注入的时候,使用的类越多,构造方法就会越长,在视觉上就会引起注意,提醒你这个类有点庞大了。因此在使用 Facades 的时候,要特别注意控制好类的大小,让类的作用范围保持短小。

在开发与 Laravel 进行交互的第三方扩展包时,建议最好选择注入 Laravel 契约 ,而不是使用 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 类实现。

不管是 Laravel 自带的 Facades,还是用户自定义的 Facades ,都继承自 Illuminate\Support\Facades\Facade 类。

门面类只需要实现一个方法:getFacadeAccessor。正是 getFacadeAccessor 方法定义了从容器中解析什么,然后 Facade 基类使用魔术方法 __callStatic() 从你的门面中调用解析对象。

在下面的例子中,调用了 Laravel 的缓存系统。通过浏览这段代码,可以假定在 Cache 类中调用了静态方法 get:

<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\Controller;
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)。

实时门面

使用实时门面,可以将应用中的任意类当做门面来使用。

为了说明如何使用这个功能,我们先看一个替代方案。例如我们假设 Podcast 模型有一个 publish 方法,尽管如此,为了发布博客,我们需要注入 Publisher 实例:

<?php
namespace App;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
     * Publish the podcast.
     *
     * @param  Publisher  $publisher
     * @return void
     */
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}

因为可以模拟注入的发布服务,所以注入发布实例到该方法后允许我们轻松在隔离状态下测试该方法。不过,这要求我们每次调用 publish 方法时,都要传递一个发布服务实例。

使用实时门面,我们可以在维持这种易于测试的前提下不必显式传递 Publisher 实例。要生成一个实时门面,在导入类前面加上 Facades 命名空间前缀即可:

<?php
namespace App;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
     * Publish the podcast.
     *
     * @return void
     */
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}

使用实时门面后,发布服务实例将会通过使用 Facades 前缀后的接口或类名在服务容器中解析。

在测试的时候,我们可以使用 Laravel 自带的门面测试辅助函数来模拟这个方法调用。

<?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;
/**
     * A test example.
     *
     * @return void
     */
public function test_podcast_can_be_published()
{
$podcast = factory(Podcast::class)->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}

Facade 类参考

下面列出了每个门面及其对应的底层类。

这是一个查找给定 Facade 类 API 文档的工具。服务容器绑定的可用键值也包含在内。

Facade 服务容器绑定
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\Repository cache
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
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate
Hash Illuminate\Contracts\Hashing\Hasher hash
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue
Queue (Base Class) Illuminate\Queue\Queue
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store
Storage Illuminate\Contracts\Filesystem\Factory filesystem
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

Laravel 底层原理:门面(Facades)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111724.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • mysql添加索引命令

    mysql添加索引命令  创建脚本1.PRIMARY  KEY(主键索引)mysql&gt;ALTER  TABLE  `table_name`  ADD  PRIMARY  KEY(  `column`  ) 2.UNIQUE(唯一索引)mysql&gt;ALTER  TABLE  `table_name`  ADD  UNIQUE(`column`) 3.INDEX(普通索引)my…

  • linux安装yarn

    linux安装yarn这里介绍使用yum的方式:先要安装node.js,用node-v可以查看是否安装了node。1、添加yarn仓库wgethttps://dl.yarnpkg.com/rpm/yarn.repo-O/etc/yum.repos.d/yarn.repo2、安装yarnyum-yinstallyarn安装完成后,yarn-v可以查看版本。…

  • 2014——我的求职(三)

    2014——我的求职(三)

  • pycharm添加搜索路径_pycharm系统找不到指定路径

    pycharm添加搜索路径_pycharm系统找不到指定路径为了能让python解释器找到我安装的python包的位置,我把该位置添加到了PYTHONPATH中,结果试了半天,pycharm中的解释器都没能检测到我那个包的存在,而在终端打开python的命令交互模式都是可以获取我在PYTHONPATH中设置的路径的。(气得我一口老血喷出来,哪位大神要是知道这是什么情况,敬请告知呀喵。感激不尽呦^_^)好了,说正事了。pycharm这么气人怎么办呢?闹了半

  • 如何搭建www服务器_网站服务器

    如何搭建www服务器_网站服务器更多内容参见个人技术博客,无广告欢迎关注搭建自己的服务器,过程大致分为3步:*购买服务器,配置系统环境*获得域名* CA认证1、购买服务器,配置系统环境经过比较,阿里云、腾讯

  • js 数组转json,json转数组[js 数组与json 互相转换]

    js 数组转json,json转数组[js 数组与json 互相转换]js数组和json经常需要相互转换,在此记录它们之间的转换代码://json字符串转数组vararray=[99,100,{“name”:”Jerry”},{“site”:”hdjc8.com”}];JSON.stringify(array);alert(array);//json字符串转数组varj…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号