# 插件研發
* * * * *
插件的介紹及概念在之前相關章節已經介紹過了,此處直接帶大家來實戰,來做一個省市縣三級聯動插件,這個是非常常用滴,希望大家看完后可以自己動手寫插件。
1.分析插件需求,選擇省或市后需要加載對應的數據,所以控制器肯定是必須要有的,這些邏輯都寫在控制器中也可以,但是作者不希望大家都這樣玩。希望盡量將邏輯封裝在邏輯層供控制器調用,所以這里就再加一個邏輯層。data目錄是存在sql語句的,所以必不可少。view 展示插件視圖,三級聯動肯定有HTML咯,有HTML的話應該也會有靜態資源,前面有數據庫操作那最后再加個數據模型。
2.通過上面的需求咱們將目錄做出來了,如下圖:

相信大家也都能看的懂每個目錄是干啥的,Region.php 實現了插件通用接口,咱們先將Region.php中的內容寫完,代碼如下:
~~~
<?php
// +---------------------------------------------------------------------+
// | OneBase | [ WE CAN DO IT JUST THINK ] |
// +---------------------------------------------------------------------+
// | Licensed | http://www.apache.org/licenses/LICENSE-2.0 ) |
// +---------------------------------------------------------------------+
// | Author | Bigotry <3162875@qq.com> |
// +---------------------------------------------------------------------+
// | Repository | https://gitee.com/Bigotry/OneBase |
// +---------------------------------------------------------------------+
namespace addon\region;
use app\common\controller\AddonBase;
use addon\AddonInterface;
/**
* 區域選擇插件
*/
class Region extends AddonBase implements AddonInterface
{
/**
* 實現鉤子
*/
public function RegionSelect($param = [])
{
$this->assign('addons_data', $param);
$this->assign('addons_config', $this->addonConfig($param));
return $this->fetch('index/index');
}
/**
* 插件安裝
*/
public function addonInstall()
{
return [RESULT_SUCCESS, '安裝成功'];
}
/**
* 插件卸載
*/
public function addonUninstall()
{
return [RESULT_SUCCESS, '卸載成功'];
}
/**
* 插件基本信息
*/
public function addonInfo()
{
return ['name' => 'Region', 'title' => '區域選擇', 'describe' => '區域三級聯動選擇插件', 'author' => 'Bigotry', 'version' => '1.0'];
}
/**
* 插件配置信息
*/
public function addonConfig($param)
{
return $param;
}
}
~~~
3.沒有數據啥都干不了,那么再將data目錄下的安裝與卸載腳本完善一下。
**安裝腳本install.sql**
~~~
INSERT INTO `ob_hook` (`name`, `describe`, `addon_list`, `status`, `update_time`, `create_time`) VALUES ('RegionSelect', '區域選擇', 'Region', '1', '0', '0');
INSERT INTO `ob_addon` (`name`, `title`, `describe`, `config`, `author`, `version`, `status`, `create_time` , `update_time`) VALUES ('Region', '區域選擇', '區域選擇插件', '', 'Bigotry', '1.0', '1', '0', '0');
-- ----------------------------
-- Table structure for `ob_region`
-- ----------------------------
DROP TABLE IF EXISTS `ob_region`;
CREATE TABLE `ob_region` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '地區名稱',
`level` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '深度',
`upid` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '父級',
`status` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=910007 DEFAULT CHARSET=utf8 COMMENT='省市縣數據表';
-- ----------------------------
-- Records of ob_region
-- ----------------------------
INSERT INTO `ob_region` VALUES ('110000', '北京市', '1', '0', '1');
INSERT INTO `ob_region` VALUES ('120000', '天津市', '1', '0', '1');
INSERT INTO `ob_region` VALUES ('130000', '河北省', '1', '0', '1');
INSERT INTO `ob_region` VALUES ('140000', '山西省', '1', '0', '1');
INSERT INTO `ob_region` VALUES ('150000', '內蒙古', '1', '0', '1');
-- ----------------------------
-- ... 此處省略N行地區插入,因為太長咯,官方源碼插件的data目錄下有蛤
-- ----------------------------
~~~
**卸載腳本uninstall.sql**
~~~
DELETE FROM ob_hook WHERE `name` = 'RegionSelect';
DELETE FROM ob_addon WHERE `name` = 'Region';
DROP TABLE IF EXISTS `ob_region`;
~~~
4.然后來弄模板,這個插件主要用于后臺研發帶省市縣聯動選擇的功能時使用,所以咱們目前只考慮兼容后臺樣式,當然也可以通過不同模塊判斷,加載不同的樣式,這個有其他模塊省市縣選擇需求時咱們加個樣式判斷就好咯。
模板 HTML代碼如下:
~~~
<div>
<select name="province_{$addons_data.name}" id="province_{$addons_data.name}" class="form-control addon-form-group-select"></select>
<select name="city_{$addons_data.name}" id="city_{$addons_data.name}" class="form-control addon-form-group-select"></select>
<select name="county_{$addons_data.name}" id="county_{$addons_data.name}" class="form-control addon-form-group-select"></select>
</div>
~~~
為什么 name 和 id 中 有 變量({$addons_data.name})?
因為為了實現不同name 或 id 操作不能的數據,總不能一個頁面只能用一個省市聯動吧,作者當年就遇到過一個婚戀系統,一個資料頁面 就 三四個省市縣聯動。。所以得支持這種一個頁面 N 次復用,就用到了變量名稱了。
這個HTML上的class form-control 是后臺的樣式,addon-form-group-select 是作者自己加的。
下面咱們在插件的靜態資源目錄 static 中創建一個樣式文件 region_style.css 在里面寫上一些調整HTML的樣式,css代碼如下:
~~~
@charset "utf-8";
.addon-form-group-select{
width: 33.33%;
float: left;
overflow: hidden;
margin-bottom: 10px;
}
~~~
5.模板也搞定了,現在該考慮怎么拿數據了。
在業務邏輯層新建文件 Index.php,先封裝一個 getRegionList 方法,參數為查詢條件,這樣就可以在控制器層進行多次查詢復用,由于省市縣這些基礎數據一般不會經常變動,可以再做個根據查詢條件緩存數據,這樣就可以大大的降低系統開銷。
數據也拿出來了,那么后面就該把數據展示出來,由于是三級聯動 需要根據上層數據獲取下層數據,帶級聯關系,但是萬變不離其宗,封裝一個通用的 option 生成方法。
下面是將 Index.php 寫完的代碼,如下:
~~~
<?php
// +---------------------------------------------------------------------+
// | OneBase | [ WE CAN DO IT JUST THINK ] |
// +---------------------------------------------------------------------+
// | Licensed | http://www.apache.org/licenses/LICENSE-2.0 ) |
// +---------------------------------------------------------------------+
// | Author | Bigotry <3162875@qq.com> |
// +---------------------------------------------------------------------+
// | Repository | https://gitee.com/Bigotry/OneBase |
// +---------------------------------------------------------------------+
namespace addon\region\logic;
use app\common\model\Addon;
/**
* 省市縣三級聯動插件邏輯
*/
class Index extends Addon
{
/**
* 組合下拉框選項信息
*/
public function combineOptions($id = 0, $list = [], $default_option_text = '')
{
$data = "<option value =''>$default_option_text</option>";
foreach ($list as $vo)
{
$data .= "<option ";
if ($id == $vo['id']) : $data .= " selected "; endif;
$data .= " value ='" . $vo['id'] . "'>" . $vo['name'] . "</option>";
}
return $data;
}
/**
* 獲取區域列表
*/
public function getRegionList($where = [])
{
$cache_key = 'cache_region_' . md5(serialize($where));
$cache_list = cache($cache_key);
if (!empty($cache_list)) : return $cache_list; endif;
$list = $this->modelRegion->getList($where, true, 'id', false);
!empty($list) && cache($cache_key, $list);
return $list;
}
}
~~~
上面的代碼實現了通用方法 getRegionList 與 combineOptions,然后看看控制器里面應該如何調用這兩個邏輯層通用方法呢。
6.作者有強迫癥,不希望出現兩次以上重復代碼塊,于是將控制器層的省市縣HTML獲取也考慮寫一個方法,那么一個方法怎么區分想獲取哪些數據呢,肯定需要傳一個上級id,咱們表里面有個 upid 實際上就是 上級id,那么上級id基本上可以滿足查詢數據的情況了,但是 一般省市縣選擇初始化的時候 會有一個提示 如 請選擇省份,因為總不能幫用戶自動選擇某地區吧,嘿嘿。咱們看到表里面 有個 level 字段,省市縣分別對應 1 2 3,那么咱們再傳一個level 就可以知道默認提示什么文字描述咯。
這些可還是不夠,因為不光要考慮 三級聯動怎么生成,還得考慮 用戶已經選擇了,還得默認選擇,所以 咱們再來一個 select_id 。
下面是將 Index.php 控制器 寫完的代碼,如下:
~~~
<?php
// +---------------------------------------------------------------------+
// | OneBase | [ WE CAN DO IT JUST THINK ] |
// +---------------------------------------------------------------------+
// | Licensed | http://www.apache.org/licenses/LICENSE-2.0 ) |
// +---------------------------------------------------------------------+
// | Author | Bigotry <3162875@qq.com> |
// +---------------------------------------------------------------------+
// | Repository | https://gitee.com/Bigotry/OneBase |
// +---------------------------------------------------------------------+
namespace addon\region\controller;
use app\common\controller\AddonBase;
/**
* 區域選擇控制器
*/
class Index extends AddonBase
{
/**
* 獲取選項信息
*/
public function getOptions()
{
$where['upid'] = input('upid', DATA_DISABLE);
$where['level'] = input('level', DATA_NORMAL);
$select_id = input('select_id', DATA_DISABLE);
$list = $this->logicIndex->getRegionList($where);
switch ($where['level'])
{
case 1: $default_option_text = "---請選擇省份---"; break;
case 2: $default_option_text = "---請選擇城市---"; break;
case 3: $default_option_text = "---請選擇區縣---"; break;
default: $this->error('省市縣 level 不存在');
}
$data = $this->logicIndex->combineOptions($select_id, $list, $default_option_text);
return $this->result($data);
}
}
~~~
7.最后一步了,零件都造好了,就差最后一步 怎么組裝起來呢,這時候js就派上用場了,下面是模板文件寫入js代碼后的完整代碼:
~~~
<link rel="stylesheet" href="__STATIC__/region_style.css">
<div>
<select name="province_{$addons_data.name}" id="province_{$addons_data.name}" class="form-control addon-form-group-select"></select>
<select name="city_{$addons_data.name}" id="city_{$addons_data.name}" class="form-control addon-form-group-select"></select>
<select name="county_{$addons_data.name}" id="county_{$addons_data.name}" class="form-control addon-form-group-select"></select>
</div>
<script type="text/javascript">
$(function(){
var province_id = "{$addons_data['province']}";
var city_id = "{$addons_data['city']}";
var county_id = "{$addons_data['county']}";
var get_options_url = '{:addons_url("region://Index/getOptions")}';
function changeProvince(province_id = 0, select_id = 0)
{
$.get(get_options_url, {upid: province_id, select_id: select_id, level : 1}, function(result){ $("#province_{$addons_data.name}").html(result.data); });
}
function changeCity(city_id = 0, select_id = 0)
{
$.get(get_options_url, {upid: city_id, select_id: select_id, level : 2}, function(result){ $("#city_{$addons_data.name}").html(result.data); });
}
function changeCounty(county_id = 0, select_id = 0)
{
$.get(get_options_url, {upid: county_id, select_id: select_id, level : 3}, function(result){ $("#county_{$addons_data.name}").html(result.data); });
}
changeProvince(0, province_id);
changeCity(province_id, city_id);
changeCounty(city_id, county_id);
$("#province_{$addons_data.name}").change(function(){ changeCity($("#province_{$addons_data.name}").val());});
$("#city_{$addons_data.name}").change(function(){ changeCounty($("#city_{$addons_data.name}").val());});
});
</script>
~~~
到此為止省市縣三級聯動插件就制作好了,那么下面來看看應該怎么使用 ^_^。
* * * * *
### 插件使用
OneBase做的是基礎架構,目前沒有地方可以選擇省市縣,那么就到文章編輯模板里面測試下剛才寫的插件是否好用。
文章列表測試三級聯動代碼,若帶默認值則 province city county 對應省市縣id:
~~~
<div class="col-md-6">
<div class="form-group">
<label>區域1</label>
<span class="">(請選擇區域1)</span>
{:hook('RegionSelect', ['name' => 'address1', 'province' => 0, 'city' => 0, 'county' => 0])}
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>區域2</label>
<span class="">(請選擇區域2)</span>
{:hook('RegionSelect', ['name' => 'address2', 'province' => 0, 'city' => 0, 'county' => 0])}
</div>
</div>
~~~
效果:

看起來比較OK,再看下帶默認值的效果:

大功告成,作者將會提交到源碼的插件目錄中供大家使用,也希望大家寫出優秀的插件共享給大家。^_^。
- 序言
- 基礎
- 安裝環境
- 安裝演示
- 規范
- 目錄
- 介紹
- 后臺介紹
- 后臺首頁
- 會員管理
- 系統管理
- 系統設置與配置管理
- 菜單管理
- 系統回收站
- 服務管理
- 插件管理
- 文章管理
- 接口管理
- 優化維護
- SEO管理
- 數據庫
- 文件清理
- 行為日志
- 執行記錄
- 統計分析
- 接口介紹
- 接口文檔
- 錯誤碼設計
- Token介紹
- 前臺介紹
- 架構
- 架構總覽
- 生命周期
- 入口文件
- 模塊設計
- 依賴注入
- 控制器架構
- 邏輯架構
- 驗證架構
- 服務架構
- 模型架構
- 行為架構
- 插件架構
- 配置
- 配置介紹
- 配置加載
- 配置擴展
- 請求
- 請求信息
- 日志
- 后臺行為日志
- 系統執行日志
- 框架日志
- 數據
- 數據庫設計
- 數據字典
- 數據庫操作
- 事務控制
- 混合操作
- 實戰
- 控制器
- 邏輯與驗證
- 視圖與模型
- 插件研發
- 服務研發
- 接口研發
- 雜項
- 數據導入導出
- 二維碼條形碼
- 郵件發送
- 云存儲服務
- 支付服務
- 短信服務
- 微信分享
- 生成海報
- 聊天室
- PJAX
- Demo
- Widget
- 附錄
- 常量參考
- 配置參考
- 函數參考
- 進階
- Redis
- 自動緩存
- 全自動緩存
- 索引
- 數據簽名
- 全自動事務
- 隊列