百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

[ Laravel 5.6 文档 ] 底层原理 —— 门面(Facades)

toyiye 2024-09-14 13:43 4 浏览 0 评论

[ Laravel 5.6 文档 ] 底层原理 —— 门面(Facades),Laravel官方描述Facade是简洁的、容易记忆的类调用,Laravel的特性基本都提供Facade类。Facade设计模式是提供统一的接口,隐藏内部实现,当你修改内部实现而不影响外观。【文章转自laravel学院】

简介

注:对门面这个概念不理解?可参考PHP 设计模式系列 —— 门面模式(Facade)。

门面为应用服务容器中的绑定类提供了一个“静态”接口。Laravel 内置了很多门面,你可能在不知道的情况下正在使用它们。Laravel 的门面作为服务容器中底层类的“静态代理”,相比于传统静态方法,在维护时能够提供更加易于测试、更加灵活、简明优雅的语法。

Laravel 的所有门面都定义在 Illuminate\Support\Facades 命名空间下,所以我们可以轻松访问到门面:

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

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

何时使用门面

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

但是,使用门面也有需要注意的地方,一个最主要的危险就是类范围蠕变。由于门面如此好用并且不需要注入,在单个类中使用过多门面,会让类很容易变得越来越大。使用依赖注入则会让此类问题缓解,因为一个巨大的构造函数会让我们很容易判断出类在变大。因此,使用门面的时候要尤其注意类的大小,以便控制其有限职责。

注:构建与 Laravel 交互的第三方扩展包时,最好注入 Laravel 契约而不是使用门面,因为扩展包在 Laravel 之外构建,你将不能访问 Laravel 的门面测试辅助函数。

门面 vs. 依赖注入

依赖注入的最大优点是可以替换注入类的实现,这在测试时很有用,因为你可以注入一个模拟或存根并且在存根上断言不同的方法。

但是在静态类方法上进行模拟或存根却行不通,不过,由于门面使用了动态方法对服务容器中解析出来的对象方法调用进行了代理,我们也可以像测试注入类实例那样测试门面。例如,给定以下路由:

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

我们可以这样编写测试来验证 Cache::get 方法以我们期望的方式被调用:

use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
 Cache::shouldReceive('get')
 ->with('key')
 ->andReturn('value');
 $this->visit('/cache')
 ->see('value');
}

门面 vs 辅助函数

除了门面之外,Laravel 还内置了许多辅助函数用于执行通用任务,比如生成视图、触发事件、分配任务,以及发送 HTTP 响应等。很多辅助函数提供了和相应门面一样的功能,例如,下面这个门面调用和辅助函数调用是等价的:

return View::make('profile');
return view('profile');

门面和辅助函数之间并不存在实质性差别,使用辅助函数的时候,可以像测试相应门面那样测试它们。例如,给定以下路由:

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

在调用底层, cache 方法会去调用 Cache 门面上的 get 方法,因此,尽管我们使用这个辅助函数,我们还是可以编写如下测试来验证这个方法以我们期望的方式和参数被调用:

use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
 Cache::shouldReceive('get')
 ->with('key')
 ->andReturn('value');
 $this->visit('/cache')
 ->see('value');
}

门面工作原理

在 Laravel 应用中,门面就是一个为容器中对象提供访问方式的类。该机制原理由 Facade 类实现。Laravel 自带的门面,以及我们创建的自定义门面,都会继承自 Illuminate\Support\Facades\Facade 基类。

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

下面的例子中,我们将会调用 Laravel 的缓存系统,浏览代码后,也许你会觉得我们调用了 Cache 的静态方法 get

<?php
namespace App\Http\Controllers;
use 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 门面。该门面作为代理访问底层 Illuminate\Contracts\Cache\Factory 接口的实现。我们对门面的所有调用都会被传递给 Laravel 缓存服务的底层实例。

如果我们查看 Illuminate\Support\Facades\Cache 类的源码,将会发现其中并没有静态方法 get

class Cache extends Facade

{

/**

* 获取组件注册名称

*

* @return string

*/

protected static function getFacadeAccessor() {

return 'cache';

}

}

Cache 门面继承 Facade 基类并定义了 getFacadeAccessor 方法,该方法的工作就是返回服务容器绑定类的别名,当用户引用 Cache 类的任何静态方法时,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();
 }
}

门面类列表

下面列出了每个门面及其对应的底层类,这对深入给定根门面的 API 文档而言是个很有用的工具。服务容器绑定键也被包含进来:

门面服务容器绑定
AppIlluminate\Foundation\Applicationapp
ArtisanIlluminate\Contracts\Console\Kernelartisan
AuthIlluminate\Auth\AuthManagerauth
Auth(实例)Illuminate\Contracts\Auth\Guardauth.driver
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BroadcastIlluminate\Contracts\Broadcasting\Factory
Broadcast(实例)Illuminate\Contracts\Broadcasting\Broadcaster
BusIlluminate\Contracts\Bus\Dispatcher
CacheIlluminate\Cache\CacheManagercache
Cache(实例)Illuminate\Cache\Repositorycache.store
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
DB(实例)Illuminate\Database\Connectiondb.connection
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
GateIlluminate\Contracts\Auth\Access\Gate
HashIlluminate\Contracts\Hashing\Hasherhash
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\Writerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager
PasswordIlluminate\Auth\Passwords\PasswordBrokerManagerauth.password
Password(实例)Illuminate\Auth\Passwords\PasswordBrokerauth.password.broker
QueueIlluminate\Queue\QueueManagerqueue
Queue(实例)Illuminate\Contracts\Queue\Queuequeue.connection
Queue(基类)Illuminate\Queue\Queue
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\RedisManagerredis
Redis(实例)Illuminate\Redis\Connections\Connectionredis.connection
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory
Response(实例)Illuminate\Http\Response
RouteIlluminate\Routing\Routerrouter
SchemaIlluminate\Database\Schema\Builder
SessionIlluminate\Session\SessionManagersession
Session(实例)Illuminate\Session\Storesession.store
StorageIlluminate\Filesystem\FilesystemManagerfilesystem
Storage (实例)Illuminate\Contracts\Filesystem\Filesystemfilesystem.disk
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator(实例)Illuminate\Validation\Validator
ViewIlluminate\View\Factoryview
View(实例)Illuminate\View\View

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码