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 測試實例

相關文章
相關標籤/搜索