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

1. 控制器的实现

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

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

 class CI_Controller {

     private static $instance;

     /**
* Constructor
*/
public function __construct()
{
self::$instance =& $this; // Assign all the class objects that were instantiated by the
// bootstrap file (CodeIgniter.php) to local class variables
// so that CI can run as one big super object.
foreach (is_loaded() as $var => $class)
{
$this->$var =& load_class($class);
} $this->load =& load_class('Loader', 'core'); $this->load->initialize(); log_message('debug', "Controller Class Initialized");
} public static function &get_instance()
{
return self::$instance;
}
}

它定义了一个静态成员变量,并在初始化时等于自己 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

一步一步重写 CodeIgniter 框架 (5) —— 实现Controller,并加载Model的更多相关文章

  1. 一步一步重写 CodeIgniter 框架 (9) —— 使用 CodeIgniter 类库

    通过前面几节的内容,我们从零开始搭建了一个非常方便的MVC框架,理解了 CodeIgniter 框架最核心的部分.然而一个框架的便利不仅仅在于提供一个MVC就可以了,它还必须具有较高的扩展性.下面将从 ...

  2. 一步一步重写 CodeIgniter 框架 (12) —— 代码再重构,回归 CI

    第一课中搭建的基本的 框架模型, 只有一个 index.php 作为执行文件,按这种方式最不稳定的因素就是路径的问题. 我们经常需要通过合适的参数,比如 load_class('output') 或 ...

  3. 一步一步重写 CodeIgniter 框架 (6) —— 实现在控制器Controller中加载View

    1. 控制器将模型类获得的数据,传递给视图进行显示,所以视图必须负责接收数据,另外重要的一点是当模型和视图分开后,多个模型的数据可以传递给一个视图进行展示,也可以说一个模型的数据在多个不同的视图中进行 ...

  4. 一步一步重写 CodeIgniter 框架 (4) —— load_class 管理多个对象实例的思路

    我们使用CodeIgniter 框架最主要是想利用其 MVC 特性,将模型.视图分开,并通过控制器进行统一控制.在尝试实现 MVC 模式之前,我们将实现其中一个对程序结构非常有用的技巧,就是 load ...

  5. 一步一步重写 CodeIgniter 框架 (3) —— 用面向对象重构代码

    前面两篇文章为了重点突出 CodeIgniter 框架的原理,程序的结构很乱,有很多全局变量,在这一课中我们采用面向对象的方法对原先代码进行重构. 到目前为止,程序主要完成的就是 URL 分析,并根据 ...

  6. 一步一步重写 CodeIgniter 框架 (1) —— url 如何映射到具体的方法

    CodeIgniter 框架最显著的特征就是 MVC 模式,它的做法就是提取 url 中的'分段', 映射到某个类的某个方法,从而由该方法来输出最终显示的页面内容.那么我们第一课中就是实现一个这样的原 ...

  7. 一步一步重写 CodeIgniter 框架 (11) —— 使用 CodeIgniter 函数库

    在完成了CI框架的类库扩展后,很自然我们就会想到函数库的扩展.函数库的扩展在 CI 中称为 helper 函数与类有不同的地方,它不能继承,只能覆盖或者添加新的函数,或者直接完全新定义的一组函数. 由 ...

  8. 插件化框架解读之so 文件加载机制(四)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680 提问 本文的结论是跟着 System.loadlibrary() ...

  9. 插件化框架解读之Android 资源加载机制详解(二)

    阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680Android提供了一种非常灵活的资源系统,可以根据不同的条件提供 ...

随机推荐

  1. C#共享内存实例 附源码

    原文 C#共享内存实例 附源码 网上有C#共享内存类,不过功能太简单了,并且写内存每次都从开头写.故对此进行了改进,并做了个小例子,供需要的人参考. 主要改进点: 通过利用共享内存的一部分空间(以下称 ...

  2. utf8_general_ci 、utf8_general_cs和utf8_bin的区别

    用了这么长时间,发现自己竟然不知道utf_bin和utf_general_ci这两者到底有什么区别..ci是 case insensitive, 即 "大小写不敏感", a 和 A ...

  3. C语言参数传递

    //--------------------单向值传递------------------------ // swap这个方法在被调用时,给形参a,b分配了空间 // 主调函数将[数值]传递给[形参] ...

  4. C#--遍历目录实例

    鉴于前面几篇博客都说了,这边就啥都不说了.直接就開始贴代码了. 1.控件解释: FolderBrowserDialog控件一个----用来显示"浏览目录"对话框 TextBox控件 ...

  5. 【Android病毒分析报告】 - ZxtdPay 吸费恶魔

    本文章由Jack_Jia编写,转载请注明出处.  文章链接:http://blog.csdn.net/jiazhijun/article/details/11581543 作者:Jack_Jia    ...

  6. MVC日期比较(转)

     /// <summary>     /// Specifies that the field must compare favourably with the named field, ...

  7. 两种方式在Tableau Desktop 中创建子弹图(Bullet Chart)

    子弹图,顾名思义是由于该类信息图的样子很想子弹射出后带出的轨道.起初,子弹图的发展是为了取代仪表盘上常见的那种里程表,时速表等基于圆形的信息表达方式.子弹图无修饰的线性表达方式使我们能够在狭小的空间中 ...

  8. BZOJ 3675: [Apio2014]序列分割( dp + 斜率优化 )

    WA了一版... 切点确定的话, 顺序是不会影响结果的..所以可以dp dp(i, k) = max(dp(j, k-1) + (sumn - sumi) * (sumi - sumj)) 然后斜率优 ...

  9. BZOJ 1231: [Usaco2008 Nov]mixup2 混乱的奶牛( dp )

    状压dp dp( x , S ) 表示最后一个是 x , 当前选的奶牛集合为 S , 则状态转移方程 : dp( x , S ) =  Σ dp( i , S - { i } )  ( i ∈ S , ...

  10. [C++参考]拷贝构造函数的参数必须是引用类型

    在C++中, 构造函数,拷贝构造函数,析构函数和赋值函数(赋值运算符重载)是最基本不过的需要掌握的知识.在effective C++中说过这么一点:拷贝构造函数的参数必须是引用类型的.但是为什么呢? ...