Laravel 創(chuàng)建自己的 Facade

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

前言

laravel 提供了一個靈活的模式愿卒,那就是 facade 祝辣。框架內(nèi)部的 DB朽缎、Auth抱完、File 等功能也有相關(guān)的 facade 實現(xiàn)。那么蟆盹,該如何寫自己的 facade 呢孩灯?

Facade 是什么?

首先逾滥,facade 并不是 laravel 獨有的東西峰档,它就是設(shè)計模式中的外觀模式(Facade)。
當(dāng)然寨昙,這里就不長篇大論去討論外觀模式的定義了讥巡。這篇文章寫的很不錯 : 設(shè)計模式(九)外觀模式Facade(結(jié)構(gòu)型)
那么舔哪,laravel 的 facade 做了什么欢顷?
同樣的, laravel 實現(xiàn)了外觀模式的開關(guān)功能捉蚤,并且使用魔術(shù)方法 __callstatic 實現(xiàn)了靜態(tài)方式調(diào)用抬驴、動態(tài)創(chuàng)建對象的功能。參考 (官方文檔)

當(dāng)然你可能覺得這些概念很抽象缆巧,都什么玩意布持。那么其實簡單的講,laravel 的 facade 就是將某些功能封裝成工具類陕悬,而且能以靜態(tài)方式調(diào)用工具類的方法题暖。

建立自己的 facade

首先、以 laravel 5.1 框架,我之前寫過的 Geoip facade 為例胧卤,說一下怎么去建立自己的 facade唯绍。

下載 geoip 擴展

geoip 是一個可以更具 IP 獲取國家、地域枝誊、城市信息的 PHP 擴展推捐,基于 maxmind 數(shù)據(jù)庫。 github 在此侧啼。

首先牛柒,為 laravel 添加 geoip 擴展。
打開 composer.json痊乾,添加 "geoip2/geoip2": "~2.0" 到 require皮壁。
項目根目錄運行 composer update ( 需要安裝 composer )更新一下,geoip 的依賴和軟件包就被下載到 vendor 文件夾中了哪审。

然后下載 geoip 依賴的數(shù)據(jù)庫蛾魄,免費庫的地址 : GeoLite2

我下載了 GeoLite2 Country 和 GeoLite2 City 庫,放到了 storage/geoipdb 中湿滓。

建立 facade滴须。

在 app 目錄下新建 Facades 文件夾,里面新建 Facades/GeoIP/GeoIP.php 和 Facades/GeoIP/Facade/GeoIP.php (建議每個功能新建一個文件夾區(qū)分叽奥,比如我這里給 GeoIP 新建一個文件夾扔水,關(guān)于GeoIP 的東西全放到這里)
注意,F(xiàn)acades/GeoIP 下的 GeoIP.php 是你要對 geoip 擴展進行封裝的類朝氓, Facades/GeoIP/Facade 下的 GeoIP.php 是你的 facade魔市,用來給 laravel 解析使用,這兩個文件可以不同名赵哲。

目錄結(jié)構(gòu)如圖:

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 現(xiàn)在只有一個方法待德,返回了一個字符串 'geoip' , 這個字符串是一個標(biāo)號,用來給 laravel 的服務(wù)提供者解析使用的枫夺。

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;

      // 經(jīng)緯度
      $data['latitude'] = $record->location->latitude;
      $data['longitude'] = $record->location->longitude;

      return $data;
    }

}

OK,現(xiàn)在 geoip 的常用功能已經(jīng)封裝到方法中了橡庞。

注冊服務(wù)

完成了 facade 的創(chuàng)建和功能封裝较坛,下面就要使用它了。自己創(chuàng)建的 facade 要在 laravel 使用是要進行注冊的毙死,以便 laraval 在啟動時能自動注入依賴(請看 laravel 的依賴注入簡介 : laravel 依賴注入 學(xué)院君)

編寫服務(wù)提供者

在 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);
        });
    }
}

上面代碼可知,服務(wù)提供者注冊時會注冊一個單例再菊,標(biāo)號為 'geoip'爪喘,也就是我們自己的 facade 返回的那個,然后回調(diào)函數(shù)會返回一個對象纠拔,也就是我們封裝 geoip 功能的那個類的實例秉剑,不明白的同學(xué)可以看看 laravel 的服務(wù)提供者和服務(wù)容器相關(guān)知識哦。(注意要 use 將 facade 和封裝類的命名空間引用一下哦)

注冊服務(wù)提供者

laravel 5.1 以上版本的話稠诲, config/app.php 中找到 providers 和 aliases 侦鹏,將你的服務(wù)提供者和 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, 然后創(chuàng)建一個對象給用戶使用劝萤,渊涝,而無需用戶自己去 new 一個對象出來。

使用

現(xiàn)在床嫌,在任何一個控制器跨释,或者路由的回調(diào)函數(shù)中,使用

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

你會發(fā)現(xiàn)厌处,facade 已經(jīng)可以好好工作了鳖谈,enjoy!

參考文章

【1】設(shè)計模式(九)外觀模式Facade(結(jié)構(gòu)型)
【2】Laravel 服務(wù)容器實例教程 —— 深入理解控制反轉(zhuǎn)(IoC)和依賴注入(DI)
【3】Laravel 服務(wù)提供者實例教程 —— 創(chuàng)建 Service Provider 測試實例

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阔涉,一起剝皮案震驚了整個濱河市蚯姆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洒敏,老刑警劉巖龄恋,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凶伙,居然都是意外死亡郭毕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門函荣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來显押,“玉大人,你說我怎么就攤上這事傻挂〕吮” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵金拒,是天一觀的道長兽肤。 經(jīng)常有香客問我套腹,道長,這世上最難降的妖魔是什么资铡? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任电禀,我火速辦了婚禮,結(jié)果婚禮上笤休,老公的妹妹穿的比我還像新娘尖飞。我一直安慰自己,他們只是感情好店雅,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布政基。 她就那樣靜靜地躺著,像睡著了一般闹啦。 火紅的嫁衣襯著肌膚如雪沮明。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天亥揖,我揣著相機與錄音珊擂,去河邊找鬼。 笑死费变,一個胖子當(dāng)著我的面吹牛摧扇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挚歧,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼扛稽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了滑负?” 一聲冷哼從身側(cè)響起在张,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎矮慕,沒想到半個月后帮匾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡痴鳄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年瘟斜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痪寻。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡螺句,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橡类,到底是詐尸還是另有隱情蛇尚,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布顾画,位于F島的核電站取劫,受9級特大地震影響匆笤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勇凭,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一疚膊、第九天 我趴在偏房一處隱蔽的房頂上張望义辕。 院中可真熱鬧虾标,春花似錦、人聲如沸灌砖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽基显。三九已至蘸吓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撩幽,已是汗流浹背库继。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窜醉,地道東北人宪萄。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像榨惰,于是被迫代替她去往敵國和親拜英。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容