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)

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python列表中中文编码的问题

    python列表中中文编码的问题

    2021年11月19日
    45
  • Java及大数据学习路线

    Java及大数据学习路线#1.JavaSE

    2022年5月12日
    43
  • 虚拟机ifconfig或ip addr不显示ip地址「建议收藏」

    虚拟机ifconfig或ip addr不显示ip地址「建议收藏」虚拟机ifconfig或ipaddr不显示ip地址报错图片:一直查不到ip地址,有重新启动很多次解决方法(1)命令查看配置文件:vi/etc/sysconfig/network-scripts/ifcfg-ens33ens33注意看这个修改的文件后缀把ONBOOT的状态no改为yes然后重启,应该就没问题了。(2):还有一种可能是因为虚拟网卡没有正常连接,解决方法是开启虚拟网卡的服务:打开任务管理器,选择服务标签,为了保险,开启所有的和vmware有关的服务检

    2022年7月27日
    8
  • matlab plot函数详解取值范围_matlab为什么plot不出来图

    matlab plot函数详解取值范围_matlab为什么plot不出来图 在matlab中,plot函数用来绘制二维图像。1.plot默认格式 plot(x,y)这种格式中,若x,y是向量,则它们必须具有相同的长度。函数将以x为横轴,绘制y。                  若x,y都是矩阵,则它们必须具有相同的尺寸,plot函数将针对x的各列绘制y的每列。更确切的说,将x和y的对应的各列取出来,绘制曲线。比如x和y分别为n*n…

    2022年10月16日
    4
  • Java基础入门笔记03——用户交互Scanner类,程序三大结构,反编译,增强for,Java中没有goto

    Java基础入门笔记03——用户交互Scanner类,程序三大结构,反编译,增强for,Java中没有goto流程控制用户交互Scanner类实现程序和人的交互java.util.ScannerScanner 类 获取用户的输入next() 获取下一个字符nextLine()获取下一行next()以空格为结束符,但是一定要读取到有效字符才可以结束,也就是刚开始遇到的空格就会抛弃,而中间遇到空格才会停止next()不能得到带有空格的字符串package scanner;import java.util.Scanner;public class Demo01 { public

    2022年8月8日
    8
  • 电力系统104规约帧报文解析

    电力系统104规约帧报文解析最近被调到电力物联网的项目组,之前一直在做移动终端的我现在在这个项目中得要重新学起了。。~_~!1.电力系统常见概念首先介绍一份ppt:https://wenku.baidu.com/view/557ceb87be23482fb5da4c84.html看着挺好。。。这边文章主要是解释一些电力系统开发中比较常见的概念,也是给自己做个笔记~~~~~~电力系统远动(powersys…

    2022年6月20日
    106

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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