模块开发
熟悉模块的安装卸载机制
开发者工具中的模块打包向导功能:
模块打包功能实际上只是帮开发者做了一些重复性的工作。
这边的打包工具,主要做的是生成Info文件夹中的install.sql,guide.json,cleanData.sql这几个文件,同时支持自动替换模块下的文件和下载压缩后的文件功能。
打包工具的工作流程:
1.选择需要打包的模块
2.导出module为当前选择模块名的所以menu表中的记录,生成json数据记录。根据menu中的记录,工具会再去比较模块名Controller这个类的相应的菜单项,如果存在public的方法,并且在菜单中没有的,则标记为可能遗漏的菜单项,提醒开发者补全菜单,如果开发者不补全菜单,则不允许进入下一步。
出于人性化考虑,工具支持忽略遗漏菜单项,开发者如果勾选上,那么也可以进入下一步
3.查找module为当前选择模块名的所有权限节点,同时允许开发者选择一些权限节点作为默认权限节点,默认的权限节点在安装的时候会自动赋给全部的用户组。不选,则需要站长在安装模块后自己去给用户组赋权。
4.查找module为当前选择模块名的所有action和action_limit
5.超找所有 `模块名` 开头的数据表,同时检测有数据的表,作为默认数据,当然开发者也可以不勾选一些表,不勾选的表将不会生成默认数据
6.最后一步,预览将会生成的3个文件,并可以选择替换或者下载zip压缩包。
至此,模块基本已经导出成功了,有一点值得注意的是,每个阶段导出的文件,我们都可以进行修改。修改后的结果才是最终生成的结果。
卸载
卸载的时候,系统会做以下操作:
1.在menu表中,清理掉模块相关菜单项
2.在auth_rule表中,清理掉模块相关的前台权限节点
3.在action表中,清理掉模块相关的用户行为
4.在action_limit表中,清理掉模块相关的用户行为限制
5.执行cleanData.sql清理掉模块相关的数据(此步只有用户选择不保留数据才会执行。一般情况下为drop表语句)
6.执行uninstall.php文件,(如果文件存在的话,一般此步骤放置一些较为复杂的操作。此文件将被require_once进去,所以运行环境为Admin/ModuleController/uninstall())
7.将module表中的模块is_setup设为0,认为模块已经被卸载。
注:所有的清除操作都是以module字段为条件进行判断的。
安装
安装的时候会做一下操作:
1.根据Info/guide.json执行操作,如果Info/guide.json存在的话:
1.清理menu表,并根据guide.menu导入菜单项
2.清理auth_rule表,并根据guide.auth_rule导入菜单项
3.根据guide.default_rule设置每个用户组的权限为默认权限
4.根据guide.action导入用户行为
5.根据guide.action_limit导入用户行为限制
6.执行install.sql
后台构建器AdminBuilder系列
后台管理页面
为了方便开发者开发自己的模块,我们设计了一套自动构建后台模板的机制,其中包括了配置页面(AdminConfigBuilder),列表页面(AdminListBuilder),以及排序页面(AdminSortBuilder),以后还会继续增加新的辅助页面,并完善各页面的功能。
通过AdminBuilder系列的类库,可以轻松构建后台界面,解放Phper,Phper的双手天生就是来写php的,不要和我提html!权限约定
凡是涉及到表单的提交,尽可能提交到本页面,通过IS_POST判断是否为表单提交。这样可以节省一次提交。
如不这么做,必须在菜单配置里加入该url的菜单,并设为隐藏,否则权限无法赋予。
AdminConfigBuilder的设计亮点
1.采用了类库的方式,大大增加了ConfigBuilder的适用面,有朋友甚至移植到前台页面实现表单的自动生成。
2.类的定义方式,可以被IDE感知,可以自动提示参数的填写。
这套完整的后台界面生成机制,让后台无需大前端参与,仅仅是phper就可完成。举例说明:
比如打算实现以下一个管理页面,但不想写任何Html代码,就是这么任性。
下面是实现代码,纯PHP
当然,AdminBuilder不仅仅于此,还可以轻松实现多种UI界面。
树形分类界面AdminTreeListBuilder
带搜索的数据表格AdminListBuilder
系统自行处理的配置页面AdminConfigBuilder
普通表单与配置表单只需增加一条代码即可轻松转换
就是这么简单。
AdminConfigBuilder
1.配置页面 AdminConfigBuilder
作用: 生成配置页或者生产表单。
通用方法
标题
public function title($title)标题修改是修改当前页面的主内容的标题部分的文字,同时也会修改浏览器标题栏中的文字
字段填充
隐藏表单文本
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
只读文本
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
单行文本输入框
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
多行文本输入框
public
function
keyTextArea(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
整数输入框
public
function
keyInteger(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
用户UID输入框
public
function
keyUid(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
状态选择框
public
function
keyStatus(
$name
=
'status'
【键名,默认为status】,
$title
=
'状态'
【标题】 ,
$subtitle
= null 【副标题】)
下拉列表框
public
function
keySelect(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】,
$options
【选项】)
【选项】为键值对的形式如:
array
(1=>
'中国'
,2=>
'美国'
);
单选框
0
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
【选项】为键值对的形式如:
array
(1=>
'中国'
,2=>
'美国'
);
多选框
2
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
【选项】为键值对的形式如:
array
(1=>
'中国'
,2=>
'美国'
);
富文本输入框
4
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
时间输入框
5
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
创建时间输入框
6
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
逻辑是否输入框
7
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
更新时间框
8
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
看板组件
9
public
function
keyHidden(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
看板组件效果如图
解析看板数组
0
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
其中数组的标准数据格式为array('data-id'=>【】,'title'=>【】); 同时可直接传进键值对。
多选框组件
1
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
多选框组件效果如图
同时支持模糊搜索
输入组组件
2
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
输入组使用示例:
3
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
效果如图:
标题输入框
4
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
ID输入框
5
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
单用户选择框
6
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
多用户选择框
7
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
单图上传
8
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
多图上传
9
public
function
keyReadOnly(
$name
,
$title
【标题】,
$subtitle
= null 【子标题】)
城市选择框
0
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
配置项分组
配置项分组的效果如下
对配置项进行分组,产生一个分组
1
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
对配置项进行分组,批量设置全部的分组
2
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
填充数据
填入数据
3
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
【数据对象】是数组形式的,一般为数据库中某个记录
添加按钮
添加确认按钮
4
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
返回按钮
5
public
function
keyText(
$name
【键名】,
$title
【标题】,
$subtitle
= null 【副标题】)
模块开发教程一
1.模块的安装卸载机制介绍
OC的模块安装机制目前来说比较简单,暂时只支持本地模块的安装卸载。
如果要能够被OC识别并允许安装卸载。开发人员需要遵守以下约定。
目录结构约定:
模块文件放置在
/Application下
每一个模块的目录结构如下
以Issue(专辑)模块为例
/Conf 配置文件 config.php 模块配置文件
/Controller 控制器
IndexController 模块入口控制器
IssueController 模块管理后台控制器
/Info 模块信息
info.php 模块信息
install.sql 模块安装sql
uninstall.sql 模块卸载sql
/Model 业务模型
IssueModle.
class
.php Issue模块
/Static 静态资源存放
/css
/js
/View 模板文件
/defalut
/Index
index.html 模块首页模板
/Public
/Widget
ueditormini.html UeditorMini编辑器小部件模板
/Widget 小部件Widget
UeditorMiniWidget.
class
.php UeditorMini编辑器小部件控制器
模块的生命周期:
安装模块
使用模块
卸载模块
安装模块
此步骤涉及到 /Info/info.php
return
array
(
//模块名
'name'
=>
'Issue'
,
//别名
'alias'
=>
'专辑'
,
//版本号
'version'
=>
'1.0.0'
,
//是否商业模块,1是,0,否
'is_com'
=> 0,
//是否显示在导航栏内? 1是,0否 暂时无效,保留项
'show_nav'
=> 1,
//模块描述
'summary'
=>
'专辑模块,适用于精品内容展示'
,
//开发者
'developer'
=>
'嘉兴想天信息科技有限公司'
,
//开发者网站
'website'
=>
'http://www.ourstu.com'
,
//前台入口,可用U函数
'entry'
=>
'Issue/index/index'
,
//后台入口
'admin_entry'
=>
'Admin/Issue/contents'
,
//zui中的icon-xxx的小图标,此处为icon-th
'icon'
=>
'th'
,
//是否允许卸载,核心模块请设为0
'can_uninstall'
=> 1
);
上面代码定义了专辑模块的模块信息,显示在模块管理中的内容为
可以点击安装按钮执行安装。
安装的过程就是导入/Info/install.sql这个数据表。
安装成功后,左侧模块列表中会多出一个模块。图标就是info.php中的th。链接地址就是Admin/Issue/contents
卸载模块
同时可以通过卸载按钮卸载文件。
卸载模块其实就是执行了uninstall.sql这个操作。
2.模块的开发
OC中多模块的开发实际上和普通的TP模块开发是一致的。这边主要介绍一下OC中的一些机制,这些机制会大大降低开发成本。
模块管理后台的开发
管理后台的开发涉及到几个知识点:
1.管理后台文件的约定
一般来说,我们要求模块的后台文件放置在Issue/Controller/IssueController.class.php这个位置。而不是放置在Admin模块下。
同时命名空间必须是Admin\Controller
有一点要注意的是,虽然我们放置在Issue模块下,但是执行这些代码还是在Admin模块中,这一点是通过映射这个文件实现的。所以大家可以完全等同于把这个文件放到了Admin下。
namespace
Admin\Controller;
use
Admin\Builder\AdminConfigBuilder;
use
Admin\Builder\AdminListBuilder;
use
Admin\Builder\AdminTreeListBuilder;
class
IssueController
extends
AdminController
{
protected
$issueModel
;
function
_initialize()
{
$this
->issueModel = D(
'Issue/Issue'
);
parent::_initialize();
}
public
function
config()
{
$admin_config
=
new
AdminConfigBuilder();
$data
=
$admin_config
->handleConfig();
$admin_config
->title(
'专辑基本设置'
)
->keyBool(
'NEED_VERIFY'
,
'投稿是否需要审核'
,
'默认无需审核'
)
->buttonSubmit(
''
,
'保存'
)->data(
$data
);
$admin_config
->display();
}
public
function
issue()
{
//显示页面
$builder
=
new
AdminTreeListBuilder();
$attr
[
'class'
] =
'btn ajax-post'
;
$attr
[
'target-form'
] =
'ids'
;
$attr1
=
$attr
;
$attr1
[
'url'
] =
$builder
->addUrlParam(U(
'setWeiboTop'
),
array
(
'top'
=> 1));
$attr0
=
$attr
;
$attr0
[
'url'
] =
$builder
->addUrlParam(U(
'setWeiboTop'
),
array
(
'top'
=> 0));
$tree
= D(
'Issue/Issue'
)->getTree(0,
'id,title,sort,pid,status'
);
$builder
->title(
'专辑管理'
)
->buttonNew(U(
'Issue/add'
))
->data(
$tree
)
->display();
}
}
2.管理后台菜单的创建
模块在被安装之后,会自动在左侧菜单创建一个入口(要求模块的配置信息中admin_entry不为空) ,这个入口是不需要菜单机制配合的。系统会自动添加。
但是,开发者在开发后台管理界面的时候往往需要用到侧栏菜单,这就需要开发者手动创建一些菜单来实现这个功能。为了兼容原生的菜单机制,我们采用了如下的变通方法来实现侧栏菜单。
1.针对某个模块创建一个隐藏的顶级菜单项,一般命名为模块名称。如Issue里,我们先要创建一个如图所示的顶级菜单项(这一步必不可少)
创建菜单的入口为 用户-》权限(分组)-》菜单-》新增
图中链接是默认的一个地址,建议和模块的后台管理入口一致,是否隐藏设为是,如果不设为是,就会在顶部栏多出一个入口
2.创建顶级菜单项之后再创建二级菜单。
上述创建的菜单会在侧栏菜单产生这么一个菜单项
在菜单管理的过程中,我们有以下两点建议,也算是开发过程中的一些经验总结。
1.对于有表单的页面,强烈建议提交到当前页面,再用IS_POST去判断是表单提交还是普通的展示表单。
此建议好处在于可以少创建一个菜单来专门用于权限处理。不需要为展示表单和处理表单创建两个权限节点。
2.对于一些ajax操作,必须为相应ajax操作的action创建对应的隐藏节点来处理权限。
因为OC中,后台的权限默认是用菜单来实现绑定,所以一些隐藏的菜单项实际上就是一个权限节点的存在。
如果开发者不针对一些ajax请求访问的action去创建隐藏菜单的话,很容易出现给了某些管理员的列表页面的管理权限之后却没有操作的权限。
模块开发教程二
3.用AdminConfigBuilder创建一个配置页面
在日常研发中,开发人员难以避免地要创建一些管理页面,比较常见的就是配置页面,这类页面的创建,OC提供了一套简单的方案。也就是很多OC开发达人一直在用的的AdminBuilder系列类库。
这边就不详讲这个类库了,详细的大家看手册吧。我这边就讲一下一般的ConfigBuilder的用法。
考虑到Issue模块的ConfigBuilder太过简单,这边我以OpenSNS的新版首页模块Home为例,讲一个较为复杂的Config。
1).创建代码文件
这边涉及到一个知识点,就是模块的AdminController的一个内部约定,遵守这个约定可以让模块的文件更加聚合,但如果你不遵守,同样也能实现功能,缺点就是文件不聚合。
方法一、不遵守约定,我们首先要在Admin模块的Controller里创建一个HomeController,这个Controller务必需要继承AdminController,因为有很多东西要处理,AdminContoller会自动处理。
方法二、遵守约定,我们在Home模块的Controller里创建一个HomeController,同样继承自AdminController。
知识点方法一的定义和方法二的代码文件必须一致。文件内容完全不受文件位置影响。这边再额外废话一句,为什么放在不同的位置结果都是一样的呢?因为OC做了一个重定向,方法一种,文件就在AdminController下,这是最符合后台的机制的方式。但是,OC考虑到模块文件的聚合,提出了方法二,方法二实现原理就是在Admin模块的EmptyController的empty方法里,去做了检测,如果发现访问的页面不存在,就会去实例对应模块的对应Controller的对应action。这边以Home为例index.php?s=/admin/home/config.html上面这个Url按照传统方式,定位点为Application/Admin/Controller/HomeController.class.php的config()方法按照方法二的存放方式,系统会发现这个定位点不存在。接下来系统会尝试去实例Application/Home/Controller/HomeController.class.php的config()方法,如果存在就调用。实际上,方法二只是把文件移动到了Home模块下,让所有的文件都放到这个模块里,增强模块内聚。文件创建好了之后,我们就需要写内部实现代码了。
2).书写代码
基本结构
namespace
Admin\Controller;
use
Admin\Builder\AdminConfigBuilder;
class
HomeController
extends
AdminController{
public
function
config() {
}
}
命名空间采用Admin\Controller,原因上面讲过了。
use只是为了引入AdminConfigBuilder下面备用。
接下来,我们实例化一个AdminConfigBuilder。
代码
$builder
=
new
AdminConfigBuilder();
定义管理页面的标题,调用builder的title方法
$builder
->title(
'首页设置'
);
到这里。页面基本框架已经构建好了。
直接访问index.php?s=/admin/home/config.html显示如图
接下来,我们开始加入各种配置项。
通过加入这些代码,我们能创建一个如图所示的配置页面
$builder
->keySingleImage(
'LOGO'
,
'Logo图标'
,
'此处不同于网站信息内的LOGO,只在首页显示'
);
$builder
->keyRadio(
'OPEN_LOGIN_PANEL'
,
'开启登陆面板'
,
'关闭则不显示登陆面板'
,
array
(1 =>
'开启'
, 2 =>
'关闭'
));
$builder
->keyText(
'ENTER_URL'
,
'随便看看Url'
,
'不填则隐藏此按钮,不留入口,支持形如weibo/index/index之类的tp写法和http://等常规写法'
);
$builder
->keyEditor(
'SUMMARY'
,
'导语'
,
'顶部导语,用一句话来介绍你的网站'
);
$builder
->group(
'内容设置'
,
'LOGO,SUMMARY,OPEN_LOGIN_PANEL,ENTER_URL'
);
首先,我们要明确,在builder里面,我们一般会用keyXXX的函数来创建页面的表单元素。
$builder
->keySingleImage(
'LOGO'
,
'Logo图标'
,
'此处不同于网站信息内的LOGO,只在首页显示'
);
上面这条代码,是创建一个单图上传,第一个参数是表单元素的name,也就是提交到后台的表单键名,第二个参数是在页面中显示的字段标题,第三个是辅助文字。
此类的方法有很多。这边就不祥解了。着重提一提group()方法。
group()方法可以实现对配置项的分组,这个方法有两种定义方式。
public
function
group(
$name
,
$list
=
array
())
public
function
groups(
$list
=
array
())
第一种,只能定义一个配置项分组
第二种能实现多个配置项分组的同时定义
如
$builder
->group(
'内容设置'
,
'LOGO,SUMMARY,OPEN_LOGIN_PANEL,ENTER_URL'
);
就定义了一个配置项分组——内容设置,其下有LOGO,SUMMARY,OPEN_LOGIN_PANEL,ENTER_URL这几个key。
以此类推。我们可以定义多个配置项分组。
代码结构
$builder
->keyXXX();
$builder
->keyXXX();
$builder
->group();
---------组1--------
$builder
->keyXXX();
$builder
->keyXXX();
$builder
->group();
---------组2--------
$builder
->keyXXX();
$builder
->keyXXX();
$builder
->group();
---------组3--------
最后,我们还需要加入一个保存按钮。保存按钮使用代码创建就是
$builder
->buttonSubmit();
最后的最后,我们还需要展示这个界面
0
$builder
=
new
AdminConfigBuilder();
好了。大功告成。
配置表单就这么生成了,不过你生成的表单可能没数据,这个先不管。到此,差不多大家心里有个数了。下面我们再讲讲一些开发中要遇到的问题。
1.怎么设置默认值?
2.怎么保存为一个配置项,到前台模块中调用到?
对于第一点,我们这边有一个办法:调用builder的data()方法,存入一个键值对数组,调用这个方法后,系统会自动根据键值对填充内容到表单里。
对于第二点,OC提供了一个handleConfig()方法来自动保存配置,同时前台也提供了一个modC('键名','默认值','Controller名')的配置项读取方法。根本不需要开发者去管数据库读写的问题。
像我们上面的
1
$builder
=
new
AdminConfigBuilder();
仅用一行代码,就完成了对表单数据的保存,同时可以用modC()函数到前台去调用。完全不用考虑复杂的逻辑,这就是builder的魅力之所在。
同样的,我们可能会涉及到第一个问题和第二个问题结合的情况:
怎么给作为配置项的表单设置默认值?
2
$builder
=
new
AdminConfigBuilder();
将handleConfig取回的data进行无值判断,最后分号前的那个值就是默认值。
当然,前台要调用的时候也有这个默认值,就需要用modC的第二个参数来给了。
这边有一个比较麻烦的地方就是modC的默认值和后台设置的默认值可能不统一,而且在多次要读取配置项的时候会需要在modC里不断设置默认值。这一点,似乎通过现在的builder机制还无法实现,只能是笨笨地多次设置。