如何建立自己的基类与控制器建立连接失败

做项目之前,一定要先部署要自己的"基类",非常的重要。打个比方,要访问会员中心相关的控制器,这类控制器是不是都要有一个“登录限制”,才让访问会员相关的控制器?
一、创建三大基类
位置:thinkphp\apps\common\controller\Base.php
作用:Base模块下的内容,Index模块,和Admin模块都可以调用
* 原始基类
namespace app\Common\
class Base extends Controller{
public function _initialize()
parent::_initialize();
echo '原始基类';
public function test1(){
return 'test1';
Index模块基类
位置:thinkphp\apps\common\controller\Base.php
作用:Index模块下的控制器,都要“继承基类”并且“调用基类”
* 前端基类
namespace app\index\
app\Common\controller\B
class IndexBase extends
public function _initialize()
parent::_initialize();
public function index()
Admin模块基类
位置:thinkphp\apps\common\controller\Base.php
作用:Admin模块下的控制器,都要“继承基类”并且“调用基类”
* 后台首页
namespace app\Admin\
use app\Admin\controller\AdminB
class Index extends AdminBase
public function _initialize()
parent::_initialize();
public function index()
return $this-&fetch();
(User模块基类,如果有会员的话,这个也必须要创建的)
创建基类的主要目的,就是“继承”与“调用”
阅读(...) 评论()Yii2(40)
先说一下Yii中的控制器是做什么用的,以及在什么地方使用.
在Yii中,当请求一个Url的时候,首先在application中获取request信息,然后由request通过urlManager解析出route,再在Module中根据route来创建controller并处理request。
如:/index.php?r=site/login。会使用SiteController里面的actionLogin动作来处理这个请求。
Yii中总共有三种控制器类
base\Controller.php& && & 这个是下面两个的基类,即这里讲的控制器
console\Controller.php& &这个是控制台控制器web\Controller.php& && &&&这个是web控制器
先看看基类base\Controller.php,在基类中大致可分为三个部分
和action相关的功能和render相关的功能其它功能
1、和action相关的函数
我们按照这些函数的调用顺序来一一说明
执行路由:public function run($route, $params = [])
* route值即可以为当前controller中的action id,
* 也可为module id/controller id/action id/这种格式
* 如果以“/”开头,将用application来处理,否则,用控制器所属模块来处理
public function run($route, $params = [])
& && &&&//先判断route中有没有“/”
& && &&&$pos = strpos($route, '/');
& && &&&if ($pos === false) {
& && && && && & //如果没有“/”,则为action id,直接调用runAction来执行这个action。如:index
& && && && &return $this-&runAction($route, $params);
& && &&&} elseif ($pos & 0) {
& && && && && & //如果“/”在中间,由当前的模块来处理这个route。如:test/index
& && && && &return $this-&module-&runAction($route, $params);
& && &&&} else {
& && && && && & //如果以“/”开头,则用当前的应用程序来处理这个route。如:/test/
& && && && &return Yii::$app-&runAction(ltrim($route, '/'), $params);
执行动作:public function runAction($id, $params = [])
* $id 为action的id,如定义的actionIndex,那么id就为Index。
public function runAction($id, $params = [])
& && &&&//创建action
& && &&&$action = $this-&createAction($id);
& && &&&if ($action === null) {
& && && && &throw new InvalidRouteException('Unable to resolve the request: ' . $this-&getUniqueId() . '/' . $id);
& && &&&Yii::trace(&Route to run: & . $action-&getUniqueId(), __METHOD__);
& && &&&if (Yii::$app-&requestedAction === null) {
& && && && &Yii::$app-&requestedAction = $
& && &&&$oldAction = $this-&
& && &&&$this-&action = $
& && &&&//用来保存当前控制器的所有父模块,顺序为由子模块到父模块
& && &&&$modules = [];
& && &&&$runAction =
& && &&&/*
& && && &* 获取当前控制器的所属模块,并执行每个模块的beforeAction来检查当前的action是否可以执行,
& && && &* 注意:getModules返回的数组顺序为:从父模块到子模块,
& && && &* 所以在执行beforeAction的时候,先检查最外层的父模块,然后检查子模块。
& && && &*
& && && &* 然而在执行afterAction的时候,顺序就反过来了,先执行子模块,最后执行父模块。
& && && &*
& && && &*/
& && &&&foreach ($this-&getModules() as $module) {
& && && && &if ($module-&beforeAction($action)) {
& && && && && & array_unshift($modules, $module);
& && && && &} else {
& && && && && & $runAction =
& && && && && &
& && && && &}
& && &&&$result =
& && &&&//如果所有的父模块都满足执行的条件
& && &&&if ($runAction) {
& && && && && & /*
& && && && && &&&* 再判断当前控制器中是beforeAction,
& && && && && &&&* 最后由生成的action对象来执行runWithParams方法
& && && && && &&&*
& && && && && &&&* 执行完后,再执行afterAction方法
& && && && && &&&*/
& && && && &if ($this-&beforeAction($action)) {
& && && && && & $result = $action-&runWithParams($params);
& && && && && & $result = $this-&afterAction($action, $result);
& && && && &}
& && &&&//执行所有父模块的afterAction
& && &&&foreach ($modules as $module) {
& && && && &/** @var Module $module */
& && && && &$result = $module-&afterAction($action, $result);
& && &&&$this-&action = $oldA
& && &&&return $
创建动作 public function createAction($id)
//由action id来创建action对象
public function createAction($id)
& && &&&//使用默认的action id ,默认值为:index
& && &&&if ($id === '') {
& && && && &$id = $this-&defaultA
& && &&&$actionMap = $this-&actions();
& && &&&if (isset($actionMap[$id])) {
& && && &&& //如果在actions方法中指定了独立的动作,则直接使用此动作。
& && && && &return Yii::createObject($actionMap[$id], [$id, $this]);
& && &&&} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
& && && &&& /*
& && && &&&& * action id由:a到z、0到9、\、-、_ 这五种字符组成,
& && &&&& && * 并且不能包含“--”
& && &&&& && * 并且不能以“-”为开头或结尾
& && &&&& && *
& && && & && * 先以“-”把id分隔为数组,再以“ ”连接到字符串,把每个单词首字母大写,最后把“ ”去掉,并和&action&连接
& && &&&& && * 如;
& && &&&& && * 1、new-post-v-4
& && &&&& && * 2、['new','post','v','4']
& && &&&& && * 3、new post v 4
& && &&&& && * 4、New Post V 4
& && &&&& && * 5、NewPostV4
& && & && && * 6、actionNewPostV4
& &&&& && && */
& && && && &$methodName = 'action' . str_replace(' ', '', ucwords(implode(' ', explode('-', $id))));
& && && && &if (method_exists($this, $methodName)) {
& && && && && & /*
& && && && && && * 如果当前控制器中存在这个actionXXX方法,
& && && && && && * 再通过反射生成方法,再次检查一遍,最后生成InlineAction
& && && && && && */
& && && && && & $method = new \ReflectionMethod($this, $methodName);
& && && && && & if ($method-&getName() === $methodName) {
& && && && && && &&&return new InlineAction($id, $this, $methodName);
& && && && && & }
& && && && &}
所以,如果一个动作在定义的时候是用骆驼格式名称的,如actionNewArticle,那么写url的时候r=site/new-article。
定义独立动作的数组:public function actions()
* 独立action定义
* 这个用来指定独立的action,返回格式为name-value的数组,name为action的id,value为action类的实现,如:
* return [
*& &&&'action1' =& 'app\components\Action1',
*& &&&'action2' =& [
*& && && &'class' =& 'app\components\Action2',
*& && && &'property1' =& 'value1',
*& && && &'property2' =& 'value2',
* 这个主要是用于在子类中重写
public function actions()
& && &&&return [];
由createAction可知,当controller在创建action的时候,会根据动作ID先在这个数组里面查找,如果找到则返回这个动作。所以这里定义的动作的优先级要大于在控制器里面定义的actionXXX函数。
绑定动作的参数:public function bindActionParams($action, $params)
* 绑定action的参数。
* 比如定义了动作 actionCrate($id,$name=null)
* 那个这个函数的作用就是从params(一般为$_GET)中提取$id,$name,
* 具体的实现在web\Controller.php和console\Controller.php中
public function bindActionParams($action, $params)
& && &&&return [];
beforeAction、afterAction,事件触发
//在具体的动作执行之前会先执行beforeAction,如果返回false,则动作将不会被执行,
//后面的afterAction也不会执行(但父模块跌afterAction会执行)
public function beforeAction($action)
& && &&&$event = new ActionEvent($action);
& && &&&$this-&trigger(self::EVENT_BEFORE_ACTION, $event);
& && &&&return $event-&isV
//当前动作执行之后,执行afterAction
public function afterAction($action, $result)
& && &&&$event = new ActionEvent($action);
& && &&&$event-&result = $
& && &&&$this-&trigger(self::EVENT_AFTER_ACTION, $event);
& && &&&return $event-&
在这个都会触发事件,beforeAction触发EVENT_BEFORE_ACTION事件,afterAction触发EVENT_AFTER_ACTION
2、和render相关的功能
获取、设置view组件:public function getView()、public function setView($view)
//获取view组件,
public function getView()
& && &&&if ($this-&_view === null) {
& && && && &$this-&_view = Yii::$app-&getView();
& && &&&return $this-&_
//设置view组件
public function setView($view)
& && &&&$this-&_view = $
渲染视图文件和布局文件(如果有布局的话):public function render($view, $params = [])
//渲染视图文件和布局文件(如果有布局的话)
public function render($view, $params = [])
& && &&&//由view对象渲染视图文件
& && &&&$output = $this-&getView()-&render($view, $params, $this);
& && &&&//查找布局文件
& && &&&$layoutFile = $this-&findLayoutFile($this-&getView());
& && &&&if ($layoutFile !== false) {
& && && && && & //由view对象渲染布局文件,
& && && && && & //并把上面的视图结果作为content变量传递到布局中,所以布局中才会有$content变量来表示
& && && && &return $this-&getView()-&renderFile($layoutFile, ['content' =& $output], $this);
& && &&&} else {
& && && && &return $
渲染视图文件,不会应用布局:public function renderPartial($view, $params = [])
//这个只渲染视图文件,不会应用布局
public function renderPartial($view, $params = [])
& && &&&return $this-&getView()-&render($view, $params, $this);
渲染文件:public function renderFile($file, $params = [])
//这个就是用来渲染一个文件,$file为文件实路径或别名路径
public function renderFile($file, $params = [])
& && &&&return $this-&getView()-&renderFile($file, $params, $this);
获取这个控制器对应的view的文件路径:public function getViewPath()
//获取这个控制器对应的view的文件路径,如@app/views/site/xxxx.php
public function getViewPath()
& && &&&return $this-&module-&getViewPath() . DIRECTORY_SEPARATOR . $this-&
查找布局文件:protected function findLayoutFile($view)
//查找布局文件
protected function findLayoutFile($view)
& && &&&$module = $this-&
& && &&&//如果当前控制器设置了布局文件,则直接使用所设置的布局文件
& && &&&if (is_string($this-&layout)) {
& && && && &$layout = $this-&
& && &&&} elseif ($this-&layout === null) {
& && && && && & //如果没有设置布局文件,则查找所有的父模块的布局文件。
& && && && &while ($module !== null && $module-&layout === null) {
& && && && && & $module = $module-&
& && && && &}
& && && && &if ($module !== null && is_string($module-&layout)) {
& && && && && & $layout = $module-&
& && && && &}
& && &&&//如果没有设置布局文件,返回false
& && &&&if (!isset($layout)) {
& && && && &
& && &&&/*
& && && &* 布局文件有三种路径写法
& && && &* 1、以“@”开头,这种会在别名路径中查找布局文件
& && && &* 2、以“/”开头,这个会从应用程序的布局文件目录下面查找布局文件
& && && &* 3、其它情况,& &这个会从当前模块的布局文件目录下查查找布局文件
& && && &*/
& && &&&if (strncmp($layout, '@', 1) === 0) {
& && && && &$file = Yii::getAlias($layout);
& && &&&} elseif (strncmp($layout, '/', 1) === 0) {
& && && && &$file = Yii::$app-&getLayoutPath() . DIRECTORY_SEPARATOR . substr($layout, 1);
& && &&&} else {
& && && && &$file = $module-&getLayoutPath() . DIRECTORY_SEPARATOR . $
& && &&&//如果布局文件有文件扩展名,返回
& && &&&if (pathinfo($file, PATHINFO_EXTENSION) !== '') {
& && && && &return $
& && &&&//加上默认的文件扩展名。
& && &&&$path = $file . '.' . $view-&defaultE
& && &&&//如果文件不存在,并且,默认的文件扩展名也不是php,则给加上php作为扩展名。
& && &&&if ($view-&defaultExtension !== 'php' && !is_file($path)) {
& && && && &$path = $file . '.php';
& && &&&return $
3、其它功能
获取当前控制器所有的父模块:public function getModules()
//获取当前控制器所有的父模块
public function getModules()
& && &&&$modules = [$this-&module];
& && &&&$module = $this-&
& && &&&while ($module-&module !== null) {
& && && && && & //由这里可知,返回的数组顺序为从父模块到子模块
& && && && &array_unshift($modules, $module-&module);
& && && && &$module = $module-&
& && &&&return $
获取控制器id:public function getUniqueId()
//返回控制器id
public function getUniqueId()
& && &&&//如果当前所属模块为application,则就为该id,否则要前面要加上模块id
& && &&&return $this-&module instanceof Application ? $this-&id : $this-&module-&getUniqueId() . '/' . $this-&
获取路由信息:public function getRoute()
//获取路由信息
public function getRoute()
& && &&&return $this-&action !== null ? $this-&action-&getUniqueId() : $this-&getUniqueId();
另外还有几个变量和2个事件
//在执行beforeAction方法时触发的事件,
//如果对事件的isValid属性设置为false,将取消action的执行
const EVENT_BEFORE_ACTION = 'beforeAction';
//在执行afterAction方法是触发的事件
const EVENT_AFTER_ACTION = 'afterAction';
//控制器id
//所属模块
//控制器中默认动作
public $defaultAction = 'index';
//布局文件,如果设置为false,则不使用布局文件
//当前下面执行的action,可在事件中根据这个action来执行不同的操作
//视图对象
private $_
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:70700次
积分:1917
积分:1917
排名:千里之外
原创:125篇
(3)(1)(1)(3)(5)(53)(8)(33)(8)(7)(1)(1)(4)基于MVC4+EasyUI的Web开发框架形成之旅--基类控制器CRUD的操作 - 伍华聪 - 博客园
随笔 - 522, 文章 - 20, 评论 - 7436, 引用 - 20
在上一篇随笔中,我对Web开发框架的总体界面进行了介绍,其中并提到了我的《Web开发框架》的控制器的设计关系,Web开发框架沿用了我的《》的很多架构设计思路和特点,对Controller进行了封装。使得控制器能够获得很好的继承关系,并能以更少的代码,更高效的开发效率,实现Web项目的开发工作,整个控制器的设计思路如下所示。
从上图的设计里面可以看到,我把主要能通过抽象封装的CRUD方法都放到了BusinessController&B, T&类里面,本文继续详细介绍这个Web框架控制器类的CRUD具体实现,以便使得大家了解我的整个Web开发框架的基类控制器的工作原理。
1、基类的插入操作
我们知道,一般常规的插入操作是很普遍的操作,那么我们在MVC的Web界面上是如何调用的,后台又是如何进行数据的处理的呢?
在MVC的View视图代码里面,我们添加数据的时候,javascript脚本代码是这样的:
var postData = $("#ffAdd").serializeArray();
$.post("/Information/Insert", postData, function (data) {
if (data = "true") {
//添加成功
1.关闭弹出层,2.刷新DataGird
$.messager.alert("提示", "添加成功");
$("#DivAdd").dialog("close");
$("#grid").datagrid("reload");
$("#ffAdd").form("clear");
//本页面的类型为【通知公告】,固定不变
$("#Category").val("通知公告");
$.messager.alert("提示", "添加失败,请您检查");
其中的serializeArray就把该表单要提交的数据序列号到一个字符串里面了,里面的数据可能类似A=a&B=b&C=c 这样的字符串里面了,通过POST调用控制器Information的Insert方法,实现数据的插入。由于控制器Information是具体业务类,因此它继承自BusinessController&B, T&,也就是会调用BusinessController&B, T&控制器的Insert方法。
如字典数据添加的界面如下所示。
那么后台的接收方法如何呢?其实后台是把数据序列化到了一个FormCollection对象的集合里面了,但是,我们还可以使用对象T(实体类),让这些数据集合赋值给对应的实体对象属性,如下就是我的后台控制器的插入方法,它的参数是一个实体类T,这样我们直接调用业务操作类就可以插入了,代码很简单易懂。
/// &summary&
/// 插入指定对象到数据库中
/// &/summary&
/// &param name="info"&指定的对象&/param&
/// &returns&执行操作是否成功。&/returns&
public virtual ActionResult Insert(T info)
{bool result = false;
if (info != null)
result = baseBLL.Insert(info);
return Content(result);
2、基类的更新操作
上面我们看了插入数据的操作,可能大家对下面介绍的数据更新操作,可能也已经有了一些了解了,其实它和插入操作的方法很类似的。
更新操作的视图View中脚本代码如下所示, 它通过控制器Information的Update方法进行更新数据。
var ID = $("#ID1").val();
var postData = $("#ffEdit").serializeArray();
$.post("/Information/Update?ID=" + ID, postData, function (date) {
if (date == "true") {
//修改成功,关闭弹出层,刷新DataGird
$.messager.alert("提示", "修改成功");
$("#DivEdit").dialog('close');
$("#grid").datagrid("reload");
$.messager.alert("提示", "修改失败,请您检查");
由于控制器Information是具体业务类,因此它继承自BusinessController&B, T&,也就是会调用BusinessController&B, T&控制器的Update方法。
和上面的插入操作一样,后台是把数据序列化到了一个FormCollection对象的集合里面了,我们可以使用类似和插入方法的操作,如下所示。
/// &summary&
/// 更新对象属性到数据库中
/// &/summary&
/// &param name="info"&指定的对象&/param&
/// &param name="id"&主键ID的值&/param&
/// &returns&执行成功返回&c&true&/c&,否则为&c&false&/c&。&/returns&
public virtual ActionResult Update(T info, string id)
bool result = baseBLL.Update(info, id);
return Content(result);
但是,如果使用以上的代码作为更新数据的代码,那么在编辑界面里,如果只是显示编辑部分表的数据,那么可能导致很多属性会被初始化为实体类的默认值,显然这样不符合我们的要求,我们可能只是进行部分更新,那么我们进行部分更新的控制器方法应该如何设计呢?
前面我们说到,数据会被序列号到一个FormCollection对象集合里面,更新方法也一样,那么我们可以把更新操作的接口定义为如下代码所示,视图操作代码不变化:
/// &summary&
/// 更新对象属性到数据库中
/// &/summary&
/// &param name="info"&指定的对象&/param&
/// &param name="id"&主键ID的值&/param&
/// &returns&执行成功返回&c&true&/c&,否则为&c&false&/c&。&/returns&
public virtual ActionResult Update(string id, FormCollection formValues)
我们可以通过调试的方法,查询到FormCollection里面的值就是我们更新界面里面的数据(注意:可能是实体类的部分数据)。但使用了这个方法后,我们还需要把FormCollection对象里面的数据转换为实体类的信息,我们才好调用BaseBLL里面的接口进行更新数据。但是不同的实体类,有不同的属性,我们如何能够抽象把他们的属性都赋值了呢?
答案是通过反射属性方式,把FormCollection里面属性的值赋值给对应实体类属性的值。下面我们来介绍下具体的代码实现了。
/// &summary&
/// 更新对象属性到数据库中
/// &/summary&
/// &param name="info"&指定的对象&/param&
/// &param name="id"&主键ID的值&/param&
/// &returns&执行成功返回&c&true&/c&,否则为&c&false&/c&。&/returns&
public virtual ActionResult Update(string id, FormCollection formValues)
T obj = baseBLL.FindByID(id);
if (obj != null)
//遍历提交过来的数据(可能是实体类的部分属性更新)
foreach (string key in formValues.Keys)
string value = formValues[key];
System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(key);
if (propertyInfo != null)
// obj对象有key的属性,把对应的属性值赋值给它(从字符串转换为合适的类型)
//如果转换失败,会抛出InvalidCastException异常
propertyInfo.SetValue(obj, Convert.ChangeType(value, propertyInfo.PropertyType), null);
bool result = baseBLL.Update(obj, id);
return Content(result);
通过对象propertyInfo的SetValue方法,可以把字符串的值,转换为实体类对应属性类型的值,顺利进行赋值。
如果是业务类需要提交一些HTML的代码,那么我们需要在具体的业务类里面,重写插入、更新方法并设置一下&[ValidateInput(false)] 标识才可以。
[ValidateInput(false)]
public override ActionResult Insert(InformationInfo info)
info.Editor = CurrentUser.N
info.EditTime = DateTime.N
return base.Insert(info);
[ValidateInput(false)]
public override ActionResult Update(string id, FormCollection formValues)
return base.Update(id, formValues);
如通知公告的内容编辑界面如下所示。
3、基类的获取对象数据方法
我们在很多接口里面,都要求获取单一对象的数据信息,我在基类接口里面定义了一个FindByID方法,就是从业务对象里面,根据主键ID信息,获取一个对象的数据,把他转换为Json传递到View视图里面使用即可。
/// &summary&
/// 查询数据库,检查是否存在指定ID的对象
/// &/summary&
/// &param name="id"&对象的ID值&/param&
/// &returns&存在则返回指定的对象,否则返回Null&/returns&
public virtual ActionResult FindByID(string id)
ActionResult result = Content("");
T info = baseBLL.FindByID(id);
if (info != null)
result = JsonDate(info);
其中的JsonDate方法是为了避免日期类型的数值在序列化中出现错误格式,包装的一个方法,如下所示。
/// &summary&
/// 返回处理过的时间的Json字符串
/// &/summary&
/// &param name="date"&&/param&
/// &returns&&/returns&
public ContentResult JsonDate(object date)
var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" };
return Content(JsonConvert.SerializeObject(date, Formatting.Indented, timeConverter));
在View视图里面使用控制器方法,绑定数据到查看界面里面的代码如下所示。
//绑定查看详细信息的方法
function BindViewInfo() {
var ID = $("#grid").datagrid('getSelections')[0].ID;
//发送请求
$.getJSON("/Information/FindByID?r=" + Math.random() + "&id=" + ID, function (info) {
$("#ID2").val(info.ID);
$("#Title2").text(info.Title);
$("#Content2").html(info.Content);
$("#Attachment_GUID2").text(info.Attachment_GUID);
$("#Editor2").text(info.Editor);
$("#EditTime2").text(info.EditTime);
ShowUpFiles(info.Attachment_GUID, 'divViewAttach');
具体效果如下所示:
4、基类删除操作方法
在GridView里面,我们提供了删除数据的按钮,具体视图里面使用的代码如下所示。
//然后确认发送异步请求的信息到后台删除数据
$.messager.confirm("删除确认", "您确认删除选定的记录吗?", function (deleteAction) {
if (deleteAction) {
$.get("/Information/DeletebyIds", postData, function (data) {
if (data == "true") {
$.messager.alert("提示", "删除选定的记录成功");
$("#grid").datagrid("reload");
$.messager.alert("提示", data);
后台控制器的基类删除方法如下所示。
/// &summary&
/// 删除多个ID的记录
/// &/summary&
/// &param name="ids"&多个id组合,逗号分开(1,2,3,4,5)&/param&
/// &returns&&/returns&
public virtual ActionResult DeleteByIds(string ids)
{bool result = false;
if (!string.IsNullOrEmpty(ids))
string[] idArray = ids.Split(new char[] { ',' });
foreach (string strId in idArray)
if (!string.IsNullOrEmpty(strId))
baseBLL.Delete(strId);
result = true;
return Content(result);
以上就是基类控制器增删改查的一些通用方法的封装,业务对象控制器类,如果有特殊的需要,可以对方法进行重写即可,非常方便使用,从而减少了很多重复编写的代码,并可以使得页面的操作统一化,提高生产效率。
基于MVC4+EasyUI的Web开发框架的系列文章:

我要回帖

更多关于 laravel建立控制器 的文章

 

随机推荐