Laravel 创建自己的 Facade

我的博客原文: http://www.qinblog.net/Article/article/7.html

前言

laravel 提供了一个灵活的模式,那就是 facade 。框架内部的 DB、Auth、File 等功能也有相关的 facade 实现。那么,该如何写自己的 facade 呢?

Facade 是什么?

首先,facade 并不是 laravel 独有的东西,它就是设计模式中的外观模式(Facade)。
当然,这里就不长篇大论去讨论外观模式的定义了。这篇文章写的很不错 : 设计模式(九)外观模式Facade(结构型)
那么,laravel 的 facade 做了什么?
同样的, laravel 实现了外观模式的开关功能,并且使用魔术方法 __callstatic 实现了静态方式调用、动态创建对象的功能。参考 (官方文档)

当然你可能觉得这些概念很抽象,都什么玩意。那么其实简单的讲,laravel 的 facade 就是将某些功能封装成工具类,而且能以静态方式调用工具类的方法。

建立自己的 facade

首先、以 laravel 5.1 框架,我之前写过的 Geoip facade 为例,说一下怎么去建立自己的 facade。

下载 geoip 扩展

geoip 是一个可以更具 IP 获取国家、地域、城市信息的 PHP 扩展,基于 maxmind 数据库。 github 在此

首先,为 laravel 添加 geoip 扩展。
打开 composer.json,添加 "geoip2/geoip2": "~2.0" 到 require。
项目根目录运行 composer update ( 需要安装 composer )更新一下,geoip 的依赖和软件包就被下载到 vendor 文件夹中了。

然后下载 geoip 依赖的数据库,免费库的地址 : GeoLite2

我下载了 GeoLite2 Country 和 GeoLite2 City 库,放到了 storage/geoipdb 中。

建立 facade。

在 app 目录下新建 Facades 文件夹,里面新建 Facades/GeoIP/GeoIP.php 和 Facades/GeoIP/Facade/GeoIP.php (建议每个功能新建一个文件夹区分,比如我这里给 GeoIP 新建一个文件夹,关于GeoIP 的东西全放到这里)
注意,Facades/GeoIP 下的 GeoIP.php 是你要对 geoip 扩展进行封装的类, Facades/GeoIP/Facade 下的 GeoIP.php 是你的 facade,用来给 laravel 解析使用,这两个文件可以不同名。

目录结构如图:

Facades/GeoIP/Facade/GeoIP.php 如下

<?php

namespace App\Facades\GeoIP\Facade;

use Illuminate\Support\Facades\Facade;

class GeoIP extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'geoip';
    }
}

注意你的 facade 现在只有一个方法,返回了一个字符串 'geoip' , 这个字符串是一个标号,用来给 laravel 的服务提供者解析使用的。

Facades/GeoIP/GeoIP.php 如下(吐槽:写的有点随意)

<?php

namespace App\Facades\GeoIP;

use GeoIp2\Database\Reader;

class GeoIP
{
    /**
     * GeoIP country db path (base on storage_path).
     *
     * @var GeoIP
     */
    private $_country_db = 'geoipdb/GeoLite2-Country.mmdb';

    /**
     * GeoIP city db path (base on storage_path).
     *
     * @var GeoIP
     */
    private $_city_db = 'geoipdb/GeoLite2-City.mmdb';

    /**
     * Instance for GeoIP .
     *
     * @var GeoIP
     */
    private $_instance;

    /**
     * Init instance.
     *
     */
    public function init($mode)
    {
        switch ($mode) {
          case 'getCountry':
            $path = $this->_country_db;
            break;
          case 'getCity':
            $path = $this->_city_db;
            break;
          default:
            break;
        }

        $this->_instance = new Reader(storage_path($path));
    }

    /**
     * Get Country infomations.
     *
     * @param  String  $ip
     * @return Array
     */
    public function getCountry($ip)
    {
      $this->init(__FUNCTION__);

      $record = $this->_instance->country($ip);

      // 国家信息
      $data['iso_code'] = $record->country->isoCode;
      $data['country_name'] = $record->country->name;
      $data['country_name_zh_cn'] = $record->country->names['zh-CN'];

      return $data;
    }
 
 /**
     * Get City infomations.
     *
     * @param  String  $ip
     * @return Array
     */
    public function getCity($ip)
    {
      $this->init(__FUNCTION__);

      $record = $this->_instance->city($ip);

      $data['iso_code'] = $record->country->isoCode;
      $data['country_name'] = $record->country->name;
      $data['country_name_zh_cn'] = $record->country->names['zh-CN'];

      // 省、州信息
      $data['sub_division_name'] = $record->mostSpecificSubdivision->name;
      $data['sub_division_name_zh_cn'] = $record->mostSpecificSubdivision->names['zh-CN'];
      $data['sub_division_code'] = $record->mostSpecificSubdivision->isoCode;

      // 城市信息
      $data['city_name'] = $record->city->name;
      $data['postal_code'] = $record->postal->code;

      // 经纬度
      $data['latitude'] = $record->location->latitude;
      $data['longitude'] = $record->location->longitude;

      return $data;
    }

}

OK,现在 geoip 的常用功能已经封装到方法中了。

注册服务

完成了 facade 的创建和功能封装,下面就要使用它了。自己创建的 facade 要在 laravel 使用是要进行注册的,以便 laraval 在启动时能自动注入依赖(请看 laravel 的依赖注入简介 : laravel 依赖注入 学院君)

编写服务提供者

在 app/Providers 下新建 FacadesServiceProvider.php
可以手动建,也可以用 artisan 命令来生成,随你喜欢。
app/Providers/FacadesServiceProvider.php 代码如下:

<?php

namespace App\Providers;

use App\Service\ApiService;
use Illuminate\Support\ServiceProvider;

// include the class facade binded
use App\Facades\GeoIP\GeoIP;

class FacadesServiceProvider extends ServiceProvider
{
    /**
     * 在容器中注册绑定。
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('geoip', function ($app) {
            return new GeoIP($app);
        });
    }
}

上面代码可知,服务提供者注册时会注册一个单例,标号为 'geoip',也就是我们自己的 facade 返回的那个,然后回调函数会返回一个对象,也就是我们封装 geoip 功能的那个类的实例,不明白的同学可以看看 laravel 的服务提供者和服务容器相关知识哦。(注意要 use 将 facade 和封装类的命名空间引用一下哦)

注册服务提供者

laravel 5.1 以上版本的话, config/app.php 中找到 providers 和 aliases ,将你的服务提供者和 facade 别名配置一下 :

providers 加入 :

App\Providers\FacadeServiceProvider::class,

aliases 加入(不用每次都写很长的命名空间前缀) :

'GeoIP'      => App\Facades\GeoIP\Facade\GeoIP::class,

对于 lumen 5.2 以上,需要在 bootstrap/app.php 中添加

$app->register(App\Providers\FacadesServiceProvider::class);

注册完毕后,每次使用 facade::function 的时候,laravel 会自动解析 facade, 然后创建一个对象给用户使用,,而无需用户自己去 new 一个对象出来。

使用

现在,在任何一个控制器,或者路由的回调函数中,使用

$res = GeoIP::getCountry('75.101.195.215');
var_dump($res);

你会发现,facade 已经可以好好工作了,enjoy!

参考文章

【1】设计模式(九)外观模式Facade(结构型)
【2】Laravel 服务容器实例教程 —— 深入理解控制反转(IoC)和依赖注入(DI)
【3】Laravel 服务提供者实例教程 —— 创建 Service Provider 测试实例

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容