前段时间使用CI做了两个小项目,对CI的流程和设计理念也有了一些新的认识。CI架构的一些基本优化这里就不做介绍了,如搬离system 文件夹等。

最近有一个稍微大一点的系统,也准备拿CI来做。设计时遇到架构上的一个问题:

我们知道,CI的工作流程大致是这样的,官网3.0的图:

一个典型的网站加载流程应该是在 controller 里添加public 方法,在方法里调用 load->view() 来显示视图,返回结果。CI 对于敏捷开发快速迭代相当友好,一个网站很容易就搭起来,而且似乎还条理清晰。但是,在有一些复杂逻辑的时候,就需要在设计时做好一些考虑。比如,网页一般都有header, main_content, footer,header 和 footer可能每个页都是一致的,每个页可能还会有menu bar之类也是一致,如果按照最简单的写法,那么每次我们都需要

$this->load->view('templates/header');
$this->load->view('body');
$this->load->view('templates/footer');

那么,如何省略每次的header和footer的load呢?很自然的,我们自定义一个Loader,在里面实现一个新的方法如 template,自动调用header和footer的载入过程,如

class MY_Loader extends CI_Loader {
public function template($template_name, $vars = array(), $return = FALSE)
{
$content = $this->view('templates/header', $vars, $return);
$content .= $this->view($template_name, $vars, $return);
$content .= $this->view('templates/footer', $vars, $return); if ($return)
{
return $content;
}
}
}

3.X 的话,可以

class MY_Loader extends CI_Loader {
public function template($template_name, $vars = array(), $return = FALSE)
{
if($return):
$content = $this->view('templates/header', $vars, $return);
$content .= $this->view($template_name, $vars, $return);
$content .= $this->view('templates/footer', $vars, $return); return $content;
else:
$this->view('templates/header', $vars);
$this->view($template_name, $vars);
$this->view('templates/footer', $vars);
endif;
}
}

那么我们调用的时候便可以

$this->load->template('body');

就行了。

还有一种办法,在官方的git wiki上,想法是每个页面有类似的模板,然后把header body footer分别载入好了之后传入变量,在view中echo 出来。所有的页面都是一个模板:

<html>
<head>
<?php
echo "\n". link_tag('assets/stylesheets/COMMON.css');
echo "\n". link_tag('assets/stylesheets/'. $theme .'.css');
?> <title>
<?php echo $title; ?>
</title>
</head> <body> <?php
?> <div id="div_everything_wrapper"> <div id="div_topbar_wrapper">
<?php echo $top_bar_view; ?>
</div> <div class="break">
</div> <div id="div_torso_wrapper"> <div id="div_navigation_menu">
<?php echo $main_menu_view; ?>
</div> <div id="div_main_content">
<?php echo $main_content_view; ?>
</div> </div> <div class="break">
</div> <div id="div_footer_wrapper">
<table width="100%">
<tr width="100%">
<td width="25%" align="left">
<b>
<!-- Can't pre-render this, as it'll throw the reported results -->
Rendered in {elapsed_time}s, and {memory_usage}.
</b>
</td>
<td width="50%" align="center">
$other_stuff
</td>
<td width="25%" align="right">
Developed using
<?php echo anchor_popup ("http://codeigniter.com/", "Code Igniter"); ?>
</td>
</tr>
</table>
</div>
</div> </body>
</html>

这两类方法,第一种方法缺乏一定的灵活性,有些时候有很多template的一些子view是共有的,难以复用。第二种方法controller的代码实际上有些臃肿。两种方法均太弱化了view的功能。这里我便有一种架构。

  1. 页面仍然存在模板,但不是一个模板,不同页面的模板可能会稍有不同

    在官方wiki的第二个方法中,其所有页面均是一个模板,这样一个问题就是难以处理多种类型的页面之间的不一致性(header中载入的一些common.css js不一样,这些就可以在不同的模板view中写好,而不用再去controller里写)

  2. CI_Loader做扩展,但是自定义的loader的传入的参数不是view的名称,而是view的内容,一个方法对应一个模板页面:
<?php
/**
* Created by PhpStorm.
* User: Sorean
* Date: 2015/5/23
* Time: 21:03
*/
defined('BASEPATH') OR exit('No direct script access allowed'); class MY_Loader extends CI_Loader{
//一种page 对应一种模板
var $footer = '';
function main_page($page_content = '', $page_data = array(), $header = '', $navbar_data = array(), $sidebar_data = array(), $footer = ''){
$page_data['header'] = $header;
$page_data['page_content'] = $page_content;
$page_data['navbar'] = parent::view('main/navbar',$navbar_data, TRUE);
$page_data['sidebar'] = parent::view('main/sidebar', $sidebar_data, TRUE);
$page_data['footer'] = $this->footer . $footer;
parent::view('main/default', $page_data);
} function login_page($data = array()){
parent::view('login/login', $data);
} function admin_page($page_content = '', $page_data = array(), $header = '', $navbar_data = array(), $sidebar_data = array(), $footer = ''){
$page_data['header'] = $header;
$page_data['page_content'] = $page_content;
$page_data['navbar'] = parent::view('admin/navbar',$navbar_data, TRUE);
$page_data['sidebar'] = parent::view('admin/sidebar', $sidebar_data, TRUE);
$page_data['footer'] = $this->footer . $footer;
parent::view('admin/default', $page_data);
} // -------------------------------------------------------------------- /**
* View Loader
*
* 每个view可以添加一个同名的js,会自动load在document 末尾
*
* @param string $view View name
* @param array $vars An associative array of data
* to be extracted for use in the view
* @param bool $return Whether to return the view output
* or leave it to the Output class
* @return object|string
*/
public function view($view, $vars = array(), $return = FALSE)
{
if(file_exists(APPPATH ."/views/$view-js.php")) {
$this->footer .= parent::view($view . '-js', $vars, TRUE);
}
return $this->_ci_load(array('_ci_view' => $view, '_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));
} }

如此,在controller中便可以对page_content做自由的订制,传入子view等等,而sidebar 和navbar的数据可以做进一步的扩展。

不同的页面模板做不同的xx_page名称,如admin_page, main_page,在controller中使用的时候,

$this->load->main_page(
$this->load->view(xxxxx, xxxxx),
array('title' => 'xxxx'), // page里的一些共有data,如标题
'',
parent::nav_bar_data());

还可根据要求在header和footer上做额外扩展

3.  对CI_Controller做两层扩展。

首先说明一下,对CI_Controller的扩展必须以MY_开头(定义在config文件中)。做多层扩展的类,必须和MY_Controller处于同一个文件内。因为CI只会识别MY_Controller.php那个文件。

有些人可能认为不需要多层扩展,这里分两层的作用是区分业务代码,在Comm_Controller里面是一些get_navbar_data(), get_xxx_comm_view()之类的函数,而MY_Controller里做一些非业务的逻辑,如session中的数据处理,超时。

4.细心的读者可能会发现,我重写了load->view方法,在其中寻找对应的js文件,存在则自动载入。

大多数view会有一些js处理文件,这些文件一般建议放在文档尾部(如果和view写在一个文件里,那么有些js的载入顺序难以保证,如我需要jQuery在document尾部载入,而view中的一些js依赖jQuery)

正如CI官网对自己的介绍所说“CodeIgniter is a powerful PHP framework with a very small footprint, built for developers who need a simple and elegant toolkit to create full-featured web applications.”CI最大的优势就在于功能齐全且方便扩展。这一套继承的逻辑问题在一个稍微大型里的系统里都会遇到,但是CI并没有官方的指引说建议这么写这么写,个人认为这正是CI最吸引人的地方。可以依据个人的想法和项目的需求做非常灵活的变化。我曾尝试过在view中写很多底层逻辑(如sidebar的active判定),也未尝觉得是一种丑陋的脱离MVC本源的做法。

Anyway,合适的才是最好的。

Codeigniter CI 框架的一些优化思考的更多相关文章

  1. CodeIgniter (CI)框架中的数据库查询汇总

    引言: 前两天业务涉及到一个拉取答题排行榜的需求,数据库里数据是这样的: 同一个人可能提交过多次成绩,所以同一个人可能会有多次记录: 同一个人提交的多次成绩中可能有至少两次成绩是一样的. 于是,查询的 ...

  2. CodeIgniter(CI)框架中的验证码

    在CodeIgniter框架中,CI本身自带了验证码,但是查看文档的时候,发现: 需要新建一个表,用来存储验证码信息.因为习惯了session存储验证码信息,所以我把我认为比较好看的验证码应用在了CI ...

  3. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  4. **【ci框架】精通CodeIgniter框架

    http://blog.csdn.net/yanhui_wei/article/details/25803945 一.大纲 1.codeigniter框架的授课内容安排 2.codeigniter框架 ...

  5. ci框架(codeigniter)Email发送邮件、收件人、附件、Email调试工具

        ci框架(codeigniter)Email发送邮件.收件人.附件.Email调试工具 Email 类         CodeIgniter 拥有强大的 Email 类来提供如下的功能: 多 ...

  6. CI框架笔记

    @update 2016-4-2 13:45:35 一.目录结构 ci_demo ├─myapp 应用主目录 │ ├─autoload.php 自定义的自动加载文件(可选) │ ├─myapp.php ...

  7. CI框架使用PHPmail插件发送QQ邮件:

    有助请顶,不好请评.0:33 2016/3/12CI框架使用PHPmail插件发送QQ邮件:发送成功,不过修改了主机参数,还包含了一个phpmail中的一个另外的文件,详见下方:参见:http://c ...

  8. PHP项目感悟 -- 从CI框架来看iOS的MVC

    其实这几天一直都想找时间把这个感悟整理出来,也是这一段一直思考的问题,因为这一段参加一个PHP后台项目的开发,框架使用的是CI,随着项目的进展,对于CI接触的也越多,但是由于理解的可能并不深刻,我也只 ...

  9. CI框架源码阅读笔记9 CI的自动加载机制autoload

    本篇并不是对某一组件的详细源码分析,而只是简单的跟踪了下CI的autoload的基本流程.因此,可以看做是Loader组件的分析前篇. CI框架中,允许你配置autoload数组,这样,在你的应用程序 ...

随机推荐

  1. PHP执行外部命令【转】

    PHP是完全支持外部命令的,但是出于安全考虑,一般很少使用. PHP提供共了3种方法调用外部命令: (1)调用执行外部命令函数(system(),exec(),passthru(),shell_exe ...

  2. linux应用之mysql数据库的安装及配置(centos)

    CentOS下Mysql数据库的安装与配置   如果要在Linux上做j2ee开发,首先得搭建好j2ee的开发环境,包括了jdk.tomcat.eclipse的安装(这个在之前的一篇随笔中已经有详细讲 ...

  3. 为什么要把页面放在 WEB-INF 路径下?

    1.基于不同的功能 JSP 被放置在不同的目录下 这种方法的问题是这些页面文件容易被偷看到源代码,或被直接调用.某些场合下这可能不是个大问题,可是在特定情形中却可能构成安全隐患.用户可以绕过Strut ...

  4. Chkrootkit安装配置教程 – Linux后门入侵检测

    rootkit从浅显的层面来讲即一种具有自我隐蔽性的后门程序,它往往被入侵者作为一种入侵工具.通过rootkit,入侵者可以偷偷控制被入侵的电脑,因此危害巨大.chkrootkit是一个Linux系统 ...

  5. vue.js 组件共用函数的方法之一

    如果我现在写一个组件pullMore,想要用到loadMore里面的方法(函数), 那么只需要在当前组件pullMore,script里面先引入组件import loadMore from './lo ...

  6. rsync(六)命令中文手册

    rsync(1) rsync(1) 名称 rsync - 一个快速.多功能的远程(和本地)文件拷贝工具 摘要 Local: rsync [OPTION...] SRC... [DEST] Access ...

  7. springboot中使用@Value读取配置文件

    一.配置文件配置 直接配置 在src/main/resources下添加配置文件application.properties 例如修改端口号 #端口号 server.port=8089 分环境配置 在 ...

  8. hibernate VS mybatis

    1: 一般来说,业务逻辑比较简单,集增删改查就可以满足需求,建议使用hibernate,而复杂的业务逻辑,尤其是多表关联查询,建议使用mybatis. 2: hibernate有更好的二级缓存机制,可 ...

  9. 如何使用最简单的方法将一个已经存在的工程中使用 cocaPodfile

    在网上搜索的使用 cocaPods 安装一些优秀的框架,搜索的博客大多步骤都是非常的麻烦,这里的方法非常的简单,本篇仅仅作为以后备用.   第一步:首先找到我们的工程,在终端中输入 cd 拖入已经存在 ...

  10. Android开发--环境搭建和调试技巧

    一:环境搭建 (1)我使用的环境是:window8+Java SDK+Eclipse+Android SDK+ADT 安装步骤:Java SDK-->Eclipse--->ADT---&g ...