zoukankan      html  css  js  c++  java
  • 一步一步重写 CodeIgniter 框架 (5) —— 实现Controller,并加载Model

    CodeIgniter 框架采用MVC模式,而MVC模式中起纽带作用的就是C(控制器),在控制器的中通过加载模型获得数据,将数据传到视图中进行展示。本课将实现在控制器中加载模型。

    1. 控制器的实现

    CodeIgniter 中控制器的作用很强大,通过继承CI_Controller 类就可以 $this->input 获得Input类的实例,其模型的调用方法是 $this->load->model('model'), 之后就可以通过 $this->model_name->调用相应模型的方法获取数据了。

    那么如何实现的呢?请看 CodeIgniter 中 CI_Controller 的源码。

     1 class CI_Controller {
     2 
     3     private static $instance;
     4 
     5     /**
     6      * Constructor
     7      */
     8     public function __construct()
     9     {
    10         self::$instance =& $this;
    11         
    12         // Assign all the class objects that were instantiated by the
    13         // bootstrap file (CodeIgniter.php) to local class variables
    14         // so that CI can run as one big super object.
    15         foreach (is_loaded() as $var => $class)
    16         {
    17             $this->$var =& load_class($class);
    18         }
    19 
    20         $this->load =& load_class('Loader', 'core');
    21 
    22         $this->load->initialize();
    23         
    24         log_message('debug', "Controller Class Initialized");
    25     }
    26 
    27     public static function &get_instance()
    28     {
    29         return self::$instance;
    30     }
    31 }

    它定义了一个静态成员变量,并在初始化时等于自己 self::$instance =& $this; 然后就可以通过 get_instance 静态函数获得该实例。

    foreach 循环将 通过 load_class 函数管理的实例对象(非常重要的对象,如Input,Output等)赋值作为该类的成员变量,也就是说 $this->input 相当于 load_class('Input‘)。所有控制器类通过继承 Controller 基类,就可以同样获得这种便利!!

    值得注意的是,与其他核心类不同, Loader  类是在这里的构造函数处进行的,说明了 Loader  类对于 Controller 的重要性。

    $this->load =& load_class('Loader', 'core'); 

    2. Loader 类的 model 实现

    Loader 类管理的 model 会比较多,上节课着重讲了 load_class 这种管理多个实例的原理,以下 model 函数就不难理解。

    按照 CodeIgniter 的管理,一般会定义几个搜索路径,所以可以在 Loader 中定义两个变量

        protected $_ci_model_paths = array();
    
        protected $_ci_models = array();

    其中 $_ci_model_paths 代表路径, $_ci_models 代表已加载的模型。

    在构造函数中,将$_ci_model_paths 初始化为 APPPATH,由于在本课中还没有分层,APPPATH 等同于当前目录,让 $_ci_model_paths = array('');

    然后定义 model 函数

    public function model($model, $name = '', $db_conn = FALSE) {
    
            if (is_array($model)) {
                foreach ($model as $babe) {
                    $this->model($babe);
                }
                return;
            }
    
            if ($model == '') {
                return;
            }
    
            // model 是否在一个文件夹中,如果是的话,则分析路径和文件名
            if (($last_slash = strrpos($model, '/')) !== FALSE) {
                $path = substr($model, 0, $last_slash + 1);
    
                $model = substr($model, $last_slash + 1);
            }
    
            if ($name = '') {
                $name = $model;
            }
    
            if (in_array($name, $this->_ci_models, TRUE)) {
                return;
            }
    
            $CI =& get_instance();
            if (isset($CI->$name)) {
                show_error('The model name you are loading is the name of a resource that is already being used: '.$name);
            }
    
            $model = strtolower($model);
    
            foreach ($this->_ci_model_paths as $mod_path) {
                if ( ! file_exists($mod_path.'models/'.$path.$model.'.php')) {
                    continue;
                }
    
                if ($db_conn !== FALSE AND ! class_exists('CI_DB')) {
                    if ($db_conn === TRUE) {
                        $db_conn = '';
                    }
    
                    $CI->load->database($db_conn, FALSE, TRUE);
                }
    
                if ( ! class_exists('CI_Model')) {
                    load_class('Model', 'core');
                }
    
                require_once($mod_path.'models/'.$path.$model.'.php');
                $model = ucfirst($model);
    
                $CI->$name = new $model();
    
                $this->_ci_models[] = $name;
                return;
            }
    
            // 找不到模型
            exit('Unable to locate the model you have specified: '.$model);
    
        }

    1)通过 is_array 判断参数是否为数组,是的话,循环加载每一个模型,这样就可以通过传递数组一次加载多个模型。(这也是一个很好的技巧哦,传参数的时候就可以既传单个值,也可以传数组)

    2)model 可以包含路径,这样更利于 model 的组织,比如用户模块的 基本信息model, 积分 model 都可以放在 user 文件夹下,所以将路径按 '/' 拆分,就可以得到二级 path 和 model 名。

    3)加载 model 后,该model 实例会作为 $this 的成员变量,用什么标识呢?如果不提供的话,默认就用 model 的名字。

       比如  $this->load->model('news_model');

      加载后,可以通过 $this->news_model 来访问加载的模型。

    4)规范化

      $this->load->model('News_model’); 这个用户想加载的类与 3)中一致,所以 $model 都会 strtolower 小写统一标记,这样不会出现两次加载了,另外实际在定义类的时候,news_model 对应的 class News_model;

      通过参考这些,我们可以提高写代码的优美度,也就是说用户可能在误输入大小写的情况下,依然保证能得到预期的效果。

    3. 测试

      根据前述讲述,针对上一节的代码,本次新加入的代码包括 Loader.php , Controller.php, Model.php( 暂时为空)

      Welcome 类要继承 CI_Controller 类如下所示(放在 controllers 目录下)

    <?php
    
    class welcome extends CI_Controller {
    
        function hello() {
            echo 'My first Php Framework!';
        }
    
        function saysomething($str) {
            $this->load->model('test_model');
    
            $info = $this->test_model->get_test_data();
    
            echo $info;
        }
    }

    为了测试 model 新建一个 models/test_model.php 文件,然后写入

    <?php
    
    class Test_model extends CI_Model {
    
    
        function get_test_data() {
            return 'People you want in our model is Zhangzhenyu';
        }
    
    
    }

    其中 CI_Model 暂时可以为空, 在 core/Model.php 下定义一个 CI_Model 的空类即可,以保证程序的正确执行。

    主执行文件也需要做相应的更改如下:

    require('core/Controller.php');
    
    function &get_instance() {
        return CI_Controller::get_instance();
    }
    
    
    
    require('controllers/'.$class.'.php');
    
    
    $CI = new $class();
    
    call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));

    4. 测试结果

    访问 http://localhost/learn-ci/index.php/welcome/hello

    输出 People you want in our model is Zhangzhenyu

    具体代码参见 https://github.com/zhenyu-whu/learn-ci

  • 相关阅读:
    路由配置系统(URLconf)
    Django常见命令
    MVC框架和MTV框架
    Django基础
    服务器程序和应用程序
    自定义web框架
    HTTP协议对收发消息的格式要求
    web框架的本质
    python国内镜像源
    Scrapy框架安装失败解决办法
  • 原文地址:https://www.cnblogs.com/zhenyu-whu/p/3254173.html
Copyright © 2011-2022 走看看