用户
 找回密码
 入住 CI 中国社区
搜索
查看: 23752|回复: 20
收起左侧

[视图] 超级无敌牛X之CI钩子

    [复制链接]
发表于 2010-3-12 10:29:36 | 显示全部楼层 |阅读模式
本帖最后由 ares333 于 2010-3-12 11:08 编辑

最近研究CI的Session无意中发现CI的钩子非常强劲和灵活。
之前学习Zend Framework感觉嵌入视图非常不错,今天就用CI的钩子实现一个
准备工作,个人建议最好看一下CI的大体运行流程,打开config.php修改$config['log_threshold'] = 2;这样在system/logs生成记录debug信息的日志文件,打开一个页面,日志文件如下(如果没有生成日志文件,尝试修改$config['base_url']为正确的值):
DEBUG - 2010-03-12 02:10:23 --> Config Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Hooks Class Initialized
DEBUG - 2010-03-12 02:10:23 --> URI Class Initialized
DEBUG - 2010-03-12 02:10:23 --> No URI present. Default controller set.
DEBUG - 2010-03-12 02:10:23 --> Router Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Output Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Input Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Global POST and COOKIE data sanitized
DEBUG - 2010-03-12 02:10:23 --> Language Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Loader Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Controller Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Session Class Initialized
DEBUG - 2010-03-12 02:10:23 --> Helper loaded: string_helper
DEBUG - 2010-03-12 02:10:23 --> Session routines successfully run
DEBUG - 2010-03-12 02:10:23 --> File loaded: E:\websites\ci/system/application/views/welcome_message.php
DEBUG - 2010-03-12 02:10:23 --> Final output sent to browser
DEBUG - 2010-03-12 02:10:23 --> Total execution time: 0.0534
正式开始
1.启用钩子
$config['enable_hooks'] = TRUE;
启用钩子之后还必须注意,本例使用了模板解析类(非常好用的类),在autoload.php中加载了
2.修改application/config/hooks.php,这里定义钩子
PHP复制代码
 
$hook['post_controller'][] = array(
                                'class'    => 'Layout',
                                'function' => 'render',
                                'filename' => 'Layout.php',
                                'filepath' => 'hooks',
                                'params'   => array()
                                );
 
复制代码

什么意思手册上说的非常清楚,不再解释
3.控制器
在具体钩子代码之前非常有必要说明一下我的控制器
我扩展了原有控制器以实现更多的功能
libraries/X_Controller.php
PHP复制代码
 
class X_Controller extends Controller{
        //默认布局文件
        var $layout = 'layouts/layout.php';
        //默认视图
        var $views=array(
                'head'=>'common/head',
                'header'=>'common/header',
                'content'=>'',
                'footer'=>'common/footer'
        );
        //与默认视图匹配的视图数据
        var $data=array(
                'head'=>array(),
                'header'=>array(),
                'content'=>array(),
                'footer'=>array()
        );
        public function X_Controller(){
                parent::Controller();
        }
}
 
/controllers/start.php
<?php
/**
 * 首页控制器
 * @author ares
 *
 */

class Start extends X_Controller {
 
        function Start()
        {
                parent::X_Controller();
        }
       
        function index()
        {
                $this->data['title']='这里是标题';
                $this->data['test']='测试一下';
        }
}
 
复制代码

这里请注意一下,借鉴Zend Framework的layout模式,与start控制器index方法对应的视图是/views/start/index.php
/views/start/index.php代码如下
{test}

默认layout   /views/layouts/layout.php
HTML复制代码
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
{head}
<link rel=stylesheet href="dcss/common.css" type="text/css">
<title>{title}</title>
</head>
<body>
{header}
<div id="content">
{content}<br />
</div>
{footer}
</body>
</html>
 
复制代码


4.具体的钩子
/hooks/Layout.php
PHP复制代码
<?php
class Layout{
        var $CI;
        var $layout = 'layouts/layout';
        var $views = array();
        var $data = array();
 
        function Layout(){
                $this->CI=&get_instance();
        }
       
        function render(){
                //从控制器中读取layout
                $this->layout=$this->CI->layout;
                //从控制器读取视图文件
                $this->views=$this->CI->views;
                //覆盖部分默认视图
                $this->views['content']=$this->CI->router->directory.$this->CI->router->class.'/'.$this->CI->router->method;
               
                //为每个视图文件准备数据
                $data=$this->CI->data;
                //覆盖部分默认数据
                $data['content']=$this->CI->data;
               
                //从控制器提取layout中用到的直接数据
                $this->data['title']=$this->CI->data['title'];
               
                //为layout准备数据
                foreach($this->views as $key=>$value){
                        $this->data[$key]=$this->CI->parser->parse($this->views[$key],$data[$key],TRUE);
                }
               
                $this->append_title();
                $this->CI->parser->parse($this->layout,$this->data);
        }
       
        //自动为每个页面的title添加一个后缀,比如 -CodeIgniter 中国开发者社区
        private function append_title(){
                $this->data['title'].=$this->CI->config->item('title_suffix');
        }
}
复制代码


原理比较简单,控制器中不用$this->load->view()或$this->parser->parse(),这些会自动完成,控制器中只需要提供数据($this->data)。
这种结构非常有用。
比如有这样一中情况,每个页面除了header,content,footer,还有些页面有左部导航条left
这时候只需要在layout.php中添加上left导航条就OK

其实既然已经有了layout那么这些header和footer什么的就不用再单独定义,新写的东西肯定有问题,但是原理绝对没问题,所以明白原理是最重要。

代码有些多,但是非常有必要仔细看一下
这些代码都是本主题用到的代码,其他不相关的代码都清除了
这只是基本实现,欢迎提出建议和意见
强烈要求加声望

评分

参与人数 4威望 +16 收起 理由
justdoit + 5 很给力!原创牛X
kuailewang + 5 独立思考 微型创新
Hex + 5 原创内容
ywqbestever + 1

查看全部评分

 楼主| 发表于 2010-3-12 11:48:57 | 显示全部楼层
经过进一步修改基本可以应用到实际

PHP复制代码
 
/libraries/X_Controller.php
<?php
class X_Controller extends Controller{
        //默认布局文件
        var $layout = 'layouts/layout.php';
        var $data=array();
        public function X_Controller(){
                parent::Controller();
                $this->validate();
                $this->load->scaffolding('');
        }
       
        //登录验证
        protected function validate(){
                //如果要显示登录窗口就不进行有效性验证
                if(strpos(uri_string(),'ares/login')!=FALSE){
                //验证系统管理员
                }elseif(strpos(uri_string(),'ares') != FALSE){
                        if($this->session->userdata ( 'admin_logged' ) == 'yes'){
                        }else{
                                redirect('ares/login');
                        }
                }
        }
}
 
controllers/start.php
/**
 * 首页控制器
 * @author ares
 *
 */

class Start extends X_Controller {
 
        function Start()
        {
                parent::X_Controller();
        }
       
        function index()
        {
                $this->data['title']='这里是标题';
                $this->data['test']='测试一下';
        }
}
 
 
config/hooks.php
$hook['post_controller'][] = array(
                                'class'    => 'Layout',
                                'function' => 'render',
                                'filename' => 'Layout.php',
                                'filepath' => 'hooks',
                                'params'   => array()
                                );
 
hooks/Layout.php
<?php
class Layout{
        var $CI;
        var $layout;
        var $content;
        var $data = array();
 
        function Layout(){
                $this->CI=&get_instance();
                //从控制器中读取layout
                $this->layout=$this->CI->layout;
                //从控制器读取数据
                $this->data=$this->CI->data;
                //确定目标视图
                $this->content=$this->CI->router->directory.$this->CI->router->class.'/'.$this->CI->router->method;
        }
       
        function render(){
                //初始化目标视图
                $this->data['content']=$this->CI->parser->parse($this->content,$this->data,TRUE);
                $this->append_title();
               
                $this->CI->parser->parse($this->layout,$this->data);
        }
        private function append_title(){
                $this->data['title'].=$this->CI->config->item('title_suffix');
        }
}
 
views/layouts/layout.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<base href="<?php echo $this->config->item('base_url');?>" />
<meta http-equiv="Content-Type" content="text/html; charset=<?php echo $this->config->item('charset');?>" />
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<link rel=stylesheet href="dcss/common.css" type="text/css">
<title>{title}</title>
</head>
<body>
<div id="header">
        <table width="100%">
                <tr>
                        <td width="74%"><h1>codehere.net</h1></td>
                        <td width="26%">登录 注册</td>
                </tr>
        </table>
        <ul id="nav">
                <li><a href="#">首页</a></li>
        </ul>
        <div class="clear_left"></div>
</div>
<div id="content">
{content}<br />
</div>
<div id="footer">
#footer
</div>
</body>
</html>
 
视图 views/start/index.php
{test}
 
 
复制代码
发表于 2010-3-12 13:14:17 | 显示全部楼层
很好很强大
发表于 2010-3-12 14:54:23 | 显示全部楼层
不错,顶你一个
发表于 2010-3-12 15:44:09 | 显示全部楼层
用钩子实现的 layout~~~~
发表于 2010-7-1 12:00:50 | 显示全部楼层
请问楼主,写日志钩子怎么写?
也就是说:每做一个添加或修改或删除操作,都要写一条日志到数据库,当然不同操作的日志信息是不一样的。
如果在每个操作方法里写日志,是个很大的问题,如果能用钩子来处理那就好了,但是我一直没弄明白,请楼主指教。先谢!
发表于 2010-7-7 17:06:53 | 显示全部楼层
我用钩子实现管理系统的权限设置
 楼主| 发表于 2010-7-8 16:14:52 | 显示全部楼层
回复 7# redtamo


    我用module实现系统的权限验证
 楼主| 发表于 2010-7-8 16:29:54 | 显示全部楼层
回复 6# aqw23


    如果非要这么实现的话会非常牵强而且不一定能实现,不用说太多废话了,用钩子实现是非常不理智的.就老老实实的写相应的函数吧.
发表于 2012-2-16 17:13:00 | 显示全部楼层
好文 收藏

本版积分规则