自动判断应该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() 方法自动判断返回呢。

时间: 2024-10-17 19:38:12

自动判断应该Ajax还是return的相关文章

彻底解决android读取中文txt的乱码(自动判断文档类型并转码

原文:http://blog.csdn.net/handsomedylan/article/details/6138400 public String convertCodeAndGetText(String str_filepath) {// 转码 File file = new File(str_filepath);                BufferedReader reader;                String text = "";             

shell脚本中实现自动判断用户有无密码

在最近完成老师布置的作业的时候遇到了如何让shell脚本中的命令自动判断一个用户是否已设置密码的问题,虽然看似不是很难的一个问题,但是在这一功能实现的过程中却包含了许多细小的而重要的知识.刚开始小编对此很是头疼,虽然我们查看一个用户是否有密码并不是很难,直接cat /etc/shadow这个文件看看密码位是否有加密的字符就行了,但是让命令自己去判断和匹配就不是很顺利了,小编上网查看后,并没有得到很好的答案,大多数都是人工查看的答案,并不适用于shell脚本中自动的判断,所以在认真对比/etc/p

jquery中ajax用return来返回值无效

jquery中,ajax返回值,有三种写法,只有其中一种是成功的 /** * async:false,同步调用 * 返回1:2 * 失败 * 分析:ajax内部是一个或多个定义的函数,ajax中return返回值,返回到ajax定义函数,而不是ajax外层的函数 */ function checkAccount1(){ var result = "1:2"; $.ajax({ url : path+'/user/checkAccount.do', type : "post&q

jQuery的ajax中return语句无法返回值

今天在做一个新需求的时候,用到jQuery的ajax来返回一个查询结果: 但是调用这个方法的时候,data有数据,调用的地方获取到的却一直都是undefined,在网上搜索了一些资料,找到了问题所在,解决方法如下: 1:属性中需要增加async:false; 2:在ajax之前声明一个变量,在success中,将data赋值给这个变量,并在ajax之后,return这个变量; 因为如果是异步执行的话,也许查询结果还没有返回来,js这边就把data返回回去了,但是只添加async:false属性却

输入人民币/美元后自动判断,输出转换后相对应的值

1 ''' 2 作者:pasaulis 3 版本:v2.0 4 日期:2018.11.7 5 功能:输入人民币/美元后自动判断,输出转换后相对应的值 6 ''' 7 in_str_value=input('请输入人民币/美元金额(格式示例:100rmb/100usd)') 8 9 usd_vs_value=6.91 10 rmb_value='' 11 usd_value='' 12 if in_str_value[-3:]=='rmb': 13 rmb_value=eval(in_str_va

通过js自动判断移动终端设备(ios\android等)

当用户用移动设备扫描一个二维码是,将扫描后的链接链接到一个页面,该页面只包含判断移动终端设备的js,判断好后自动跳转到对应的链接 或下载对应的内容. html代码如下: 1 <script> 2 var browser = { 3 versions: function() { 4 var u = navigator.userAgent, app = navigator.appVersion; 5 return {//移动终端浏览器版本信息 6 trident: u.indexOf('Tride

python自动开发之(ajax)第二十天

1.Django请求的生命周期 路由系统 -> 试图函数(获取模板+数据=>渲染) -> 字符串返回给用户 2.路由系统 /index/ -> 函数或类.as_view() /detail/(\d+) -> 函数(参数) 或 类.as_view()(参数) /detail/(?P<nid>\d+) -> 函数(参数) 或 类.as_view()(参数) /detail/ -> include("app01.urls") /detai

关于ajax中return false无效的问题

onSubmit="return checkForm();" 方法中: 在ajax里直接写return false,是无效的. 因为它返回的是ajax中的success方法,而不是checkForm(). 因此可以设置一个全局变量用来做判断,实现停止的效果. 注意:ajax一定要是同步的. -------------------------------------------form表单代码---------------------------------------- <for

自动判断手机版和pc版

<html><head><title>欢迎来到手机版</title><script>var ua = navigator.userAgent;var US = {    Android:function () {   //安卓        return ua.match(/Android/i)?true:false;    },    BlackBerry:function() {  //黑莓        return ua.match(/B