Laravel 建立本身的 Facade

个人博客原文: http://www.qinblog.net/Articl...

前言

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

Facade 是什么?

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

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

创建本身的 facade

首先、以 laravel 5.1 框架,我以前写过的 Geoip facade 为例,说一下怎么去创建本身的 facade。git

下载 geoip 扩展

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

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

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

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

创建 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 解析使用,这两个文件能够不一样名。设计模式

目录结构如图:app

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 测试实例

相关文章
相关标签/搜索