• Codeigniter CI 框架的一些优化思考


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

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

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

    CodeIgniter application flow

    一个典型的网站加载流程应该是在 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 "
    ". link_tag('assets/stylesheets/COMMON.css');
        echo "
    ". 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,合适的才是最好的。

  • 相关阅读:
    数据结构第四篇——线性表的链式存储之双向链表
    基本概念之引用赋值需要注意什么?
    基本概念之将引用作为函数的参数有哪些特点?
    基本概念之什么是引用?
    基本概念之模板类有什么优势?
    我的第一篇博文
    为CentOS 6 配置本地YUM源
    为CentOS 6 配置本地YUM源
    poj 1990 MooFest
    [置顶] 学生信息管理系统“重复设置”问题
  • 原文地址:https://www.cnblogs.com/Sorean/p/4524849.html
Copyright © 2020-2023  润新知