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