标签归档:codeigniter

codeigniter视图中调用控制器中的方法

学过asp.net的同学,一定会对在页面中直接调用this的方法有点儿情有独钟……
但是由那个迁移到CI框架发现在试图里的$this找不到对象了……今天解决这个问题。
首先分析CI的视图渲染原理,
每个控制器都有一个loader对象,名为load,调用loader的view方法,来渲染视图文件,同时向视图文件中传递变量……
我们找不到的变量$this应该也是这个时候被传进去,这样的话解决思路也就有了吧!
因为CI是支持重写核心类的,我们就重新loader的view方法,将当前的控制器传进去。
但是,查看一下loader对象的代码发现其中根本没有当前的控制器对象,恼人……
这样我们就得在初始化load对象的时候给他一个字段了,也就是代表当前的控制器吧!
我是把他放到控制器的构造函数中了,那个时候loader对象已经被初始化了,只要把当前的控制器对象传进去就好,
大概是这样的代码:
$this->load->controller = $this;
当时每个都这么写,感觉有点儿不爽吧!改动一下,写一个父控制器,构造函数中传递当前控制器,由子类传递吧!

class MY_Controller extends CI_Controller {
    public function __construct($controller) {
        $this->load->controller = $controller;
    }

}

这样每一个控制器的代码应该是这样的:

   class Sample extends MY_Controller {
       public function __construct(){
            parent::__construct($this);
       }
  }

重写的loader部分方法是这样的:

   class MY_Loader extends CI_Loader {
          public function view($view, $vars = array(), $return = FALSE) {
                        $view = $this->controllerName . '/' . $view;
                        $vars["cself"] = $this->controller;
                        return parent::view($view, $vars , $return);
         }
   }

这样在视图中就多了$cself对象,为了和self区分,所以用了这个名字……
在视图中$cself这个东西就代表了当前的控制器。

CI简单实现Widget

周末看了看wordpress,感觉到旁边的小工具很好用,然后回想drupal,yii中都有那么个东西,然而我们目前使用的CI中却没有,所以简单实现了一个。
同事之前的实现是在views中用require的方法实现的。感觉这样不爽,而且那样就缺少了和数据库之间的交互,就算你可以用当前控制器传递过来的数据,但是总归有些东西还是分开的比较好。
想法是这样的,widget作为一个libary的形式存在,这样就可以用CI的load了,然后控制器中传递键值型数组,分别配置controller,method。这样我们将指定一个默认的叫做default的widget。想要多个widget,那么就调用他的register吧!传递的参数包括一个名字和一堆参数,该参数数组和构造函数中的参数数组是一样的。
这样我们也不用特意的实现继承,而且不用改变CI的开发思路。比较简单吧!最后在需要的地方调用Widget的run方法。就可以输出那个控制器的那一部分了。
run方法有一个名称参数,显示之前需要在之前用register注册过该名字的widget,否则将报错。该参数默认为default。
类的代码如下:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
/**
 * 单独区块的渲染
 * @author istrone
 */
class Widget {
 
 
    /**
     * 控制器
     * @var string
     */
    private $_controller;
 
    /**
     * 方法名
     * @var string
     */
    private $_method;
 
    /**
     * 参数
     * @var array
     */
    private $_params;
 
    /**
     * 构造函数
     * @param array $params 传递的参数包括controller,method,params的键的数组,
     */
    public function __construct($params=array()){
        if(count($params)!=0)
            $this->init('default', $params);
    }
 
    /**
     * 注册widget
     * @param string $name widget的名字
     * @param array $params  传递的参数包括controller,method,params的键的数组
     */
    public function register($name,$params){
        $this->init($name, $params);
    }
 
    /**
     * 类的内部数据初始化
     * @param string $name  widget的名字
     * @param array $params 传递的参数包括controller,method,params的键的数组
     */
    private function init($name,$params){
        if(array_key_exists('controller', $params) && array_key_exists('method', $params)){
            extract($params);
            if(class_exists($controller)){
                $c = new $controller();
                if(is_a($c, 'CI_Controller') && method_exists($c, $method)){
                    $this->_controller[$name] = $controller;
                    $this->_method[$name] = $method;
                    $this->_params[$name] = isset($params['params']) ? $params: NULL;
                }
            }
        }else{
            show_error('您传递的参数不合法!');
        }
    }
 
    /**
     * 运行输出区块
     */
    public function run($name='default'){
        if(array_key_exists($name, $this->_controller)){
            $c = $this->_controller[$name];
            $m = $this->_method[$name];
            $c = new $c();
            $c->$m($this->_params[$name]);
        }else {
            show_error('您的widget調用沒有初始化!');
        }
    }

}

将以上文件保存为widget.php,放到application下的library中,然后使用。简单使用如下:

$this->load->library('widget',array('controller'=>'welcome','method'=>'test','params'=>array()));     //其中params可以省略
$this->widget->run();                                                                                 //默认渲染default的widget
$this->widget->register('first',array('controller'=>'welcome','method'=>'test','params'=>array()));
$this->load->view('welcome_message');
$this->widget->run('first');                                                                           //渲染first的widget

谈PHP中的钩子

钩子,英文为hooks。在程序中应用相当广泛,但是究竟什么是钩子呢?本人介绍一下目前本人对钩子的理解和相关心得。

假如有这么一段程序流:

function fun(){
     funA();
     funB();
}

fun函数正常的执行顺序,肯定是执行完funA,然后执行funB,然后fun函数就结束了。但是,假如我们想对函数做一些变化。比如说,fun是一个解析函数,我们希望后期可以提供丰富的解析函数,而究竟用哪个函数解析,我们希望在配置文件中配置。这个时候就可以发挥钩子的力量了。

我们可以在function fun(){}
中加入一个挂钩点H,然后再执行H这个函数之前,将钩子函数配置好,我么就可以根据需要来解析了。

例如:

$h=config_item("parser_fun") ;                           //从配置文件中获得相应的配置信息
function fun($data){
    global $h;
   return  $h();
}

当然前提,我们得有自己的解析函数。
除此意外,PHP还可以根据字符串提供自己的类,然后调用类的一个方法,传递某些参数,这些就为PHP程序的编写,以及后期的维护扩展奠定了,相当坚实的基础。
类的实现方法,大致如下:
$c=get_class_name(); //获得类的名字
$m=get_method_name(); //获得方法的名字
$k=$c->$m(); //执行类的某一个方法
钩子在PHP中应用还是相当广泛的,就目前笔者所知的Codeigniter和Drupal中钩子的应用做出如下介绍:
CodeIgniter中钩子的应用,主要是将所有钩子应用给了一个类Hook类,他向开发者对CI进行扩展,CI提供了几个挂钩点,在CodeIgniter的整个执行过程中,加入一些开发者自己的函数,而在Hook类的内部,根据外部config.php和hooks.php中的相应配置,获得相应的类或者单独的函数,然后执行之。当然这些类或者方法都是开发者自己写的。这样就可以让开发者用的更加舒心,感觉比较舒服,毕竟每个开发者应该都想在一个框架中加入更多的自己的东西。详细情况可以参看CodeIgniter官方文档,关于钩子的那一节:CodeIgniter钩子应用.
而在Drupal中钩子的应用更是无处不在,他本身就是靠钩子维护了一个体系,Module,Block,Node各个部分的实现,都是经过根本的hook函数,然后将函数名经过相应的改变,然后调用该函数得到了整个的hook结构,甚至于到显示层theme的实现,都覆盖了theme主题的api,更加令人佩服的一件事是,他居然还可以用可视化的角度,来实现相应的theme钩子,这样就将主题的开发分为了开发者和设计者两大类,设计者对可视化更敏感,而开发者对函数更为敏感。两者都可以从各自的角度,对drupal进行相应的改变。岂不快哉!