自动判断应该Ajax还是return
起因
最近回顾以前的代码,发现一个偶尔会见到的现象。一个类里面的方法可能需要Ajax返回,也有可能需要函数return。这个现象发生在网站MVC中的 逻辑层(或模型层),示例如下。IndexCtrl是控制器负责渲染页面,ProCtrl是逻辑器负责读取处理数据,A函数是实例化一个类,M函数是读取数据表的意思。现在只是简单的页面输出。
class IndexCtrl extends Ctrl{
function index(){
$proList = A('Pro')->getList();
$this->assign('proList',$proList)
->display();
}
} class ProCtrl extends Ctrl{
function getList(){
$proList = M('pro')->where("isMain='1'")->select();
return $proList;
}
}
现在来了一个管理后台中,需要用Ajax获取这些首页产品列表。怎么改呢?直接再加一个 function getListAjax(); 然后读取同样的数据库,做同样的操作?这显然不科学,同样的逻辑不应该被实现两次。
那就定义一个函数 getListAjax() 里面调用自身的 getList() 然后再Ajax返回。这样看起来似乎也没太大问题,但是当这种类似的场景增多的时候,岂不是所有的方法都有一个ajax的副本?
这样的话,就应该对 getList 函数进行改造了。怎么改呢? 加一个可选参数 $isReturn 默认值为FALSE,此时为AJAX请求返回JSON;调用者调用时传入参数值为TRUE,函数进行return。代码如下:
class IndexCtrl extends Ctrl{
function index(){
$proList = A('Pro')-> getList( TRUE );
$this->assign('proList',$proList)
->display();
}
} class ProCtrl extends Ctrl{
function getList( $isReturn=FALSE ){
$proList = M('pro')->where("isMain='1'")->select();
if($isReturn){
return $proList;
}else{
$this->ajaxReturn($proList);
}
}
}
这样的话,基本上实现了一个函数可以同时拥有两种返回方式,一种是AJAX,一种是函数return。但是这存在很多明显的问题:
1、需要修改调用者,调用者需要传递参数TRUE才能实现函数返回;
2、如果该函数本身就有参数,那加上这个附加参数就会显得很臃肿。
3、不自动,基本属于重复手写状态。
寻觅
根据观察到的现象,我们发现这里判断的关键是 $isReturn 变量,这个变量是true还是false到底有没有办法做到自动识别呢?那么自动识别的前提是什么呢?根据项目的实际情况,我定出了规则 ,就是 直接访问这个方法的URL(如http://localhost/Pro/getList)则使用AJAX返回,访问其它URL则使用函数return。
那怎么做呢?项目中使用的是Thinkphp框架,它里面有几个预定义魔法常量 MODULE_NAME,CONTROLLER_NAME,ACTION_NAME,分别表示当前访问的模块名(Home、Admin、Mobile等)、控制器名、操作名。而PHP原生也有几个魔法常量,__CLASS__、__FUNCTION__表示当前访问的类名和方法名。所以只要判断 模块名+控制器名==类名、操作名==方法名,就可以得出是URL直接访问的,使用AJAX返回,否则使用return。代码如下:
class ProCtrl extends Ctrl{
function getList(){
$proList = M('pro')->where("isMain='1'")->select(); $ctrlName = MODULE_NAME.'\Controller\\'.CONTROLLER_NAME.'Controller';
// Admin\Controller\ProController
if($ctrlName==__CLASS__ && ACTION_NAME==__FUNCTION__){
$this->ajaxReturn($proList);
}else{
return $proList;
}
}
}
封装
这样就完了吗?当然不是,这么长的一个判断总不能每个都复制一遍吧,肯定要把它封装起来成为一个公共函数。这个封装看起来很简单嘛,直接弄去一个函数里就完了。然而,too native!因为PHP的这两个魔术常量是会变的。当你把这段代码抽到一个函数中时(例 isNowAction() ),__CLASS__ 就变成了空字符串,__FUNCTION__ 就变成了 'isNowAction' 。好吧,既然普通函数不行,那有没有别的方法呢?
一、C语言中有一个叫做“内联函数”的概念,就是说这个函数虽然是写出来了,但是编译的时候是把它作为调用者的一部分直接编译到该函数中,而普通函数是通过返回跳转的(如汇编指令 RET 、 JMP)。所以,按理说这样的话这两个魔术常量就会像预期一样得出我们想要的值。然而,PHP里面并没有内联函数这个东西,并没有 inline 关键字 。。。
二、一技不成又生一技,有一个东西就做“宏定义”。C语言里面很多时候是用来封装一个小函数的,比如 #define pyth(x,y) sqrt(x*x+y*y) ,可以这样用来宏定义一个函数,或者说是简写一个函数。所以我也按照这样的方式写了一个(用了匿名函数,这很JS)
define('isNowAction()', function(){
$ctrlName = MODULE_NAME.'\Controller\\'.CONTROLLER_NAME.'Controller';
if($ctrlName==__CLASS__ && ACTION_NAME==__FUNCTION__){
return TRUE;
}else{
return FALSE;
}
});
但是发现这个并没有成功执行。翻了一下PHP的文档才知道这PHP的define并不能定义函数
还真不得不说,有时候特性太少真是一个麻烦事啊。难道这样就没有办法了吗?在函数里设两个参数,让调用者把__CLASS__和__FUNCTION__传过来?但这样的封装只是聊胜于无,并不理想。或者可以这样想,既然没有办法定义特殊的函数,那能不能有办法在函数里翻出调用者的信息呢?
功夫不负,还真有!有一个函数名为 debug_backtrace() ,可以找到调用者的信息。如图中红色箭头所指, 这个数组的第二项中的function和class正是调用者的函数名和类名。
封装好的函数代码如下:
//是否为当前模块下的控制器下的方法,常用于判断是return还是ajax
function isNowAction(){
// var_dump( debug_backtrace() );
$ctrlName = MODULE_NAME.'\Controller\\'.CONTROLLER_NAME.'Controller';
$className = debug_backtrace()[1]['class'];
$funcName = debug_backtrace()[1]['function'];
if($ctrlName==$className && ACTION_NAME==$funcName){
return TRUE;
}else{
return FALSE;
}
}
当然还可以直接封装到逻辑层Ctrl基类中,作为一个基础方法
class Ctrl {
......//框架原来写好的一些代码
protected function reJax($data){
$ctrlName = MODULE_NAME.'\Controller\\'.CONTROLLER_NAME.'Controller';
$className = debug_backtrace()[1]['class'];
$funcName = debug_backtrace()[1]['function'];
if($ctrlName ==$className && ACTION_NAME==$funcName){
$this->ajaxReturn($data);
}else{
// echo 'return';
return $data;
}
}
}
这样的话 ProCtrl 可以非常简洁,而又能自动判断是应该AJAX返回还是return
class ProCtrl extends Ctrl{
function getList(){
$proList = M('pro')->where("isMain='1'")->select();
return $this->reJax($proList);
}
}
继承问题
本来以为,这个函数到此为止就算是结束了。然而并没有。为什么呢?继承的问题。比如 Admin模块里的 ProCtrl类的getList()方法 继承自Home中的ProCtrl类,那么debug_backtrace() 得出来的 class 的类名将会是 Home\ProCtrl,也就是得到的是父类的名字而不是自己的名字,这个问题用 __CLASS__ 魔法变量也是一样存在。不知这个算不算是PHP语言的一个BUG呢?
再看到PHP文档中下面的评论,确实有说到__CLASS__的这个问题,而与此很类似的代替方法是使用 get_class($this) 函数。注意到这里有个参数是$this,也就是说当前类,所以最终我们的这个封装的方法只能写在 Controller 基类中,而无法写在公共函数方法中。最终代码如下:
class Ctrl {
......//框架原来写好的一些代码
protected function reJax($data){
$ctrlName = MODULE_NAME.'\Controller\\'.CONTROLLER_NAME.'Controller';
$className = get_class($this);
$funcName = debug_backtrace()[1]['function'];
if($ctrlName ==$className && ACTION_NAME==$funcName){
$this->ajaxReturn($data);
}else{
// echo 'return';
return $data;
}
}
}
再说两句
万万没想到,想要实现一个如此基础的自动化功能,扯出了这么多一堆概念和技术,从C语言、汇编到PHP再到对象的问题,中间还有类似JS的影子。
最后,可能有人会问,折腾了这么久,这个到底有多大的用处呢?如果同时有 网页版(需要页面渲染) 和 APP(需要JSON数据),同一个功能是写两份代码呢,还是直接使用这个 reJax() 方法自动判断返回呢。
自动判断应该Ajax还是return的更多相关文章
- Andoid自动判断输入是电话,网址或者Email的方法----Linkify的应用!
本节要讲的是,当我们在一个EditText输入电话或者网址还是Email的时候,让Android自动判断,当我们输入的是电话,我们点击输入内容将调用打电话程序,当我们输入是网址点击将打开浏览器程序.而 ...
- 彻底解决android读取中文txt的乱码(自动判断文档类型并转码
原文:http://blog.csdn.net/handsomedylan/article/details/6138400 public String convertCodeAndGetText(St ...
- onsubmit校验表单时利用ajax的return false无效解决方法
代码: function checkNewEmail(){ var re_email=new RegExp("\\w+@\\w+\\.\\w+\\.?\\w*"); var new ...
- onsubmit校验表单时利用ajax的return false无效解决方法-转
原来的代码 function checkNewEmail(){ var re_email=new RegExp("\\w+@\\w+\\.\\w+\\.?\\w*"); ...
- 判断post,ajax,get请求的方法
判断post,ajax,get请求的方法 define('IS_GET',isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] ...
- jquery中ajax用return来返回值无效
jquery中,ajax返回值,有三种写法,只有其中一种是成功的 /** * async:false,同步调用 * 返回1:2 * 失败 * 分析:ajax内部是一个或多个定义的函数,ajax中ret ...
- jQuery的ajax中return语句无法返回值
今天在做一个新需求的时候,用到jQuery的ajax来返回一个查询结果: 但是调用这个方法的时候,data有数据,调用的地方获取到的却一直都是undefined,在网上搜索了一些资料,找到了问题所在, ...
- Pace.js – 超赞的页面加载进度自动指示和 Ajax 导航效果
在页面中引入 Pace.js 和您所选择主题的 CSS 文件,就可以让你的页面拥有漂亮的加载进度和 Ajax 导航效果.不需要挂接到任何代码,自动检测进展.您可以选择颜色和多种效果,有简约,闪光灯, ...
- Andoid自动判断输入是电话,网址或者Email的方法--Linkify
Andoid自动判断输入是电话,网址或者Email的方法----Linkify的应用!http://blog.csdn.net/android_tutor/article/details/500016 ...
随机推荐
- visual formatting model (可视化格式模型)【持续修正】
概念: visual formatting model,可视化格式模型 The CSS visual formatting model is an algorithm that processes a ...
- C# ListView点击列头进行排序
/// <summary> /// This class is an implementation of the 'IComparer' interface. /// </summa ...
- “RazorEngine.Templating.TemplateCompilationException”类型的异常在 RazorEngine.NET4.0.dll 中发生,但未在用户代码中进行处理
错误信息: "RazorEngine.Templating.TemplateCompilationException"类型的异常在 RazorEngine.NET4.0.dll 中 ...
- Ionic 简单操作
在使用 Ionic 之前要安装 Nodejs,Cordova . Java 下载Java 网站.Java 默认安装在 C:\Program Files\Java 文件目录. Android 下载And ...
- JavaWeb_day04搜索_乱码_路径_转发重定向_cookie
本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! 搜索功能 DAO层都是一些数据库的增删改查操作 Ser ...
- JAVA 入门第二章 (面对对象)
本渣渣鸽了一个月终于有时间更新.因为有c++基础,学起来这章还是比较简单的,本章我觉得是程序猿质变课程,理解面向对象的思想,掌握面向对象的基本原则以及 Java 面向对象编程基本实现原理,熟练使用封装 ...
- 如何在VS 2010中使用 VS2013的解决方案(转)
今天要用VS2010打开VS2013,一直觉得VS2010到VS2012只是界面上扁平化的改变,平台工具集有改变但很大程度上可能向上兼容.在网上搜了一些文章,其中有一篇说到一个观点: 从 ...
- eclipse启动tomcat无法访问
eclipse启动tomcat无法访问 症状: tomcat在eclipse里面能正常启动,而在浏览器中访问http://localhost:8080/不能访问,且报404错误.同时其他项目页面也不能 ...
- 金融行业的BI应用分析
商业智能是一种提高企业智能化的手段,它可以满足企业发展的需要.提高企业的竞争力.同时,对于提高金融行业的风险管理.提升对外服务的质量都能够起到关键性的作用. 在市场竞争和银行业务转型期间,商业智能对于 ...
- iOS 网络流量统计
在开发中,有时候需要获取流量统计信息.研究发现:通过函数getifaddrs来得到系统网络接口的信息,网络接口的信息,包含在if_data字段中, 有很多信息, 但我现在只关心ifi_ibytes, ...