[李景山php]thinkphp核心源码注释|Model.class.php

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <[email protected]>
// +----------------------------------------------------------------------
namespace Think;
/**
 * ThinkPHP Model模型类
 * 实现了ORM和ActiveRecords模式
 */
class Model {
    
    // 操作状态
    const MODEL_INSERT          =   1;      //  插入模型数据
    const MODEL_UPDATE          =   2;      //  更新模型数据
    const MODEL_BOTH            =   3;      //  包含上面两种方式
    const MUST_VALIDATE         =   1;      // 必须验证
    const EXISTS_VALIDATE       =   0;      // 表单存在字段则验证
    const VALUE_VALIDATE        =   2;      // 表单值不为空则验证
    // 开始的 变量 定义
    // 据说 const 比 那里的 define 里面 效果要好一些, 貌似在 class里面只能用 const 定义的。

    // 当前数据库操作对象
    protected $db               =   null; // 这里要 获取 数据库操作对象
    // 数据库对象池
    private   $_db             =   array(); // 操作对象池  估计是给到分布式 连接使用的
    // 主键名称
    protected $pk               =   'id'; // 就是这里,给定了默认的 那个。
    // 主键是否自动增长
    protected $autoinc          =   false;    // 默认 是否 会自动 增长
    // 数据表前缀
    protected $tablePrefix      =   null;// 数据库表前缀,然而这个基本上都被自己重新定义了
    // 模型名称
    protected $name             =   ''; // 模型的开始
    // 数据库名称
    protected $dbName           =   '';// 数据库 名称了
    //数据库配置
    protected $connection       =   '';// 数据库 配置
    // 数据表名(不包含表前缀)
    protected $tableName        =   ''; // 数据表
    // 实际数据表名(包含表前缀)
    protected $trueTableName    =   '';// 组合后表名
    // 最近错误信息
    protected $error            =   '';// 错误信息
    // 字段信息
    protected $fields           =   array();// 可以获取到字段信息
    // 数据信息
    protected $data             =   array(); // 产生出来的 数据
    // 查询表达式参数
    protected $options          =   array();// 参数 开始
    protected $_validate        =   array();  // 自动验证定义
    protected $_auto            =   array();  // 自动完成定义
    protected $_map             =   array();  // 字段映射定义  用到的不多
    protected $_scope           =   array();  // 命名范围定义  这里没怎么用到
    // 是否自动检测数据表字段信息
    protected $autoCheckFields  =   true; // 是否开启自己的自动检测
    // 是否批处理验证
    protected $patchValidate    =   false;// 是否处理验证
    // 链操作方法列表
    protected $methods          =   array('strict','order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index','force');
    // 链式操作的 白名单
    // 第一阶段: 代码 变量 常量 定义
    // 总结:到了最后的这个,发现了 其实在类里面,也是先准备一个 各种数据的,开始。





















    /**
     * 得到当前的数据对象名称
     * @access public
     * @return string
     */
    public function getModelName() {
    
        if(empty($this->name)){
            // 继承 自己的 get_class($this)
            $name = substr(get_class($this),0,-strlen(C('DEFAULT_M_LAYER'))); //    'DEFAULT_M_LAYER'       =>  'Model', // 默认的模型层名称
            if ( $pos = strrpos($name,'\\') ) {
   //有命名空间
                $this->name = substr($name,$pos+1);// 获取其 对象
            }else{
                $this->name = $name;
            }
        }
        return $this->name;
    }
    // 回调方法 初始化模型
    protected function _initialize() {
    }
    /**
     * 切换当前的数据库连接
     * @access public
     * @param integer $linkNum  连接序号
     * @param mixed $config  数据库连接信息
     * @param boolean $force 强制重新连接
     * @return Model
     * $this->db(0,empty($this->connection)?$connection:$this->connection,true);
     */
    public function db($linkNum='',$config='',$force=false) {
    
        if('' === $linkNum && $this->db) {
            return $this->db;
        }// 这种就是返回 当前的 数据库了,普通的调用是没有的

        if(!isset($this->_db[$linkNum]) || $force ) { // 如果没有当前序号的连接, 或者可以强制 重启连接
            // 创建一个新的实例
            if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持读取配置参数
                $config  =  C($config);
            }// 重新 配置 $config
            $this->_db[$linkNum]            =    Db::getInstance($config); // 返回数据库 的连接
            // 获取个 数据库的 实例化
        }elseif(NULL === $config){ // 如果配置为空的
            $this->_db[$linkNum]->close(); // 关闭数据库连接
            unset($this->_db[$linkNum]);
            return ;
        }
        // 进行数据库连接 的实例化

        // 切换数据库连接
        $this->db   =    $this->_db[$linkNum];
        $this->_after_db(); // 钩子函数
        // 字段检测
        if(!empty($this->name) && $this->autoCheckFields)    $this->_checkTableInfo();
        // 开始了 那里的 检测表
        return $this;
    }
    // 数据库切换后回调方法
    protected function _after_db() {
    }
    /**
     * 自动检测数据表信息
     * @access protected
     * @return void
     */
    protected function _checkTableInfo() {
    
        // 如果不是Model类 自动记录数据表信息
        // 只在第一次执行记录
        if(empty($this->fields)) { // 没有记录开始
            // 如果数据表字段没有定义则自动获取
            if(C('DB_FIELDS_CACHE')) { // 'DB_FIELDS_CACHE'       =>  true,        // 启用字段缓存
                $db   =  $this->dbName?:C('DB_NAME'); // 'DB_NAME'               =>  '',          // 数据库名
                $fields = F('_fields/'.strtolower($db.'.'.$this->tablePrefix.$this->name));// 读取其缓存 数值
                // 快速文件数据读取和保存 针对简单类型数据 字符串、数组
                if($fields) {
                    $this->fields   =   $fields;
                    if(!empty($fields['_pk'])){
                        $this->pk       =   $fields['_pk'];
                    }
                    return ;
                }// 更新 当前的 存储
            }
            // 每次都会读取数据表信息
            $this->flush(); // 刷新一下 缓存
        }
    }
    /**
     * 获取字段信息并缓存
     * @access public
     * @return void
     */
    public function flush() {
    
        // 缓存不存在则查询数据表信息
        $this->db->setModel($this->name);
        $fields =   $this->db->getFields($this->getTableName());
        if(!$fields) { // 无法获取字段信息
            return false;
        }
        $this->fields   =   array_keys($fields);
        unset($this->fields['_pk']);
        foreach ($fields as $key=>$val){
            // 记录字段类型
            $type[$key]     =   $val['type'];
            if($val['primary']) {
                // 增加复合主键支持
                if (isset($this->fields['_pk']) && $this->fields['_pk'] != null) {
                    if (is_string($this->fields['_pk'])) {
                        $this->pk   =   array($this->fields['_pk']);
                        $this->fields['_pk']   =   $this->pk;
                    }
                    $this->pk[]   =   $key;
                    $this->fields['_pk'][]   =   $key;
                } else {
                    $this->pk   =   $key;
                    $this->fields['_pk']   =   $key;
                }
                if($val['autoinc']) $this->autoinc   =   true;
            }
        }
        // 记录字段类型信息
        $this->fields['_type'] =  $type;

        // 2008-3-7 增加缓存开关控制
        if(C('DB_FIELDS_CACHE')){
            // 永久缓存数据表信息
            $db   =  $this->dbName?:C('DB_NAME');
            F('_fields/'.strtolower($db.'.'.$this->tablePrefix.$this->name),$this->fields);
        }
    }// 就是 刷新个字段呗。
    /**
     * 架构函数
     * 取得DB类的实例对象 字段检查
     * @access public
     * @param string $name 模型名称
     * @param string $tablePrefix 表前缀
     * @param mixed $connection 数据库连接信息
     */
    public function __construct($name='',$tablePrefix='',$connection='') {
    
        // 其实 我们经常使用的 M();这里的 应该就是实例化这里的
        // 模型初始化
        $this->_initialize(); // 是空的。 此处没有任何代码,留出了很多代码接口,
        // 获取模型名称 M()
        if(!empty($name)) {
            if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
                list($this->dbName,$this->name) = explode('.',$name); // 应该是 数据库名   数据表名
            }else{
                $this->name   =  $name;
            }
        }elseif(empty($this->name)){
            $this->name =   $this->getModelName();
        }
        // 产生 两个
        // $this->name  这个其实是数据表 名字
        // $this->dbName  数据库名称

        // 设置表前缀
        if(is_null($tablePrefix)) {
   // 前缀为Null表示没有前缀
            $this->tablePrefix = '';
        }elseif('' != $tablePrefix) {
            $this->tablePrefix = $tablePrefix;
        }elseif(!isset($this->tablePrefix)){ // 实际中,会执行这个
            $this->tablePrefix = C('DB_PREFIX');// 这个才是
        }
        // 这里的代码执行了
        // 数据库初始化操作
        // 获取数据库操作对象
        // 当前模型有独立的数据库连接信息
        $this->db(0,empty($this->connection)?$connection:$this->connection,true); // 进行连接 操作。
        //切换当前的数据库连接
    }
    // 总结:你一个函数 折腾出这么多事情来,呵呵

























    /**
     * 设置数据对象的值
     * @access public
     * @param string $name 名称
     * @param mixed $value 值
     * @return void
     */
    public function __set($name,$value) {
    
        // 设置数据对象属性
        $this->data[$name]  =   $value;
    }// 普通 默认设置

    /**
     * 获取数据对象的值
     * @access public
     * @param string $name 名称
     * @return mixed
     */
    public function __get($name) {
    
        return isset($this->data[$name])?$this->data[$name]:null;
    }// 普通 默认设置

    /**
     * 检测数据对象的值
     * @access public
     * @param string $name 名称
     * @return boolean
     */
    public function __isset($name) {
    
        return isset($this->data[$name]);
    }// 普通 默认设置

    /**
     * 销毁数据对象的值
     * @access public
     * @param string $name 名称
     * @return void
     */
    public function __unset($name) {
    
        unset($this->data[$name]);
    }// 普通 默认设置

    /**
     * 利用__call方法实现一些特殊的Model方法
     * @access public
     * @param string $method 方法名称
     * @param array $args 调用参数
     * @return mixed
     */
    public function __call($method,$args) {
    
        if(in_array(strtolower($method),$this->methods,true)) { // 链式 操作里面的代码
            // 连贯操作的实现
            $this->options[strtolower($method)] =   $args[0];
            return $this;
        }elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){ // 统计代码
            // 统计查询的实现
            $field =  isset($args[0])?$args[0]:'*';
            return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method);
        }elseif(strtolower(substr($method,0,5))=='getby') { // 特殊的一个调用
            // 根据某个字段获取记录
            $field   =   parse_name(substr($method,5));
            $where[$field] =  $args[0];
            return $this->where($where)->find();
        }elseif(strtolower(substr($method,0,10))=='getfieldby')  {
   // 这个可以
            // 根据某个字段获取记录的某个值
            $name   =   parse_name(substr($method,10));
            $where[$name] =$args[0];
            return $this->where($where)->getField($args[1]);
        }elseif(isset($this->_scope[$method])){
   // 命名范围的单独调用支持
            return $this->scope($method,$args[0]);
        }else{
            E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
            return;
        }
    }// 普通 默认设置
    // 符合标准 就执行

    /**
     * 数据类型检测
     * @access protected
     * @param mixed $data 数据
     * @param string $key 字段名
     * @return void
     */
    protected function _parseType(&$data,$key) {
    
        if(!isset($this->options['bind'][':'.$key]) && isset($this->fields['_type'][$key])){
            $fieldType = strtolower($this->fields['_type'][$key]);
            if(false !== strpos($fieldType,'enum')){
                // 支持ENUM类型优先检测
            }elseif(false === strpos($fieldType,'bigint') && false !== strpos($fieldType,'int')) {
                $data[$key]   =  intval($data[$key]);
            }elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){
                $data[$key]   =  floatval($data[$key]);
            }elseif(false !== strpos($fieldType,'bool')){
                $data[$key]   =  (bool)$data[$key];
            }
        }
    }// 数据类型的 数据转换
    /**
     * 对保存到数据库的数据进行处理
     * @access protected
     * @param mixed $data 要操作的数据
     * @return boolean
     */
     protected function _facade($data) {
    

        // 检查数据字段合法性
        if(!empty($this->fields)) {
            if(!empty($this->options['field'])) {
                $fields =   $this->options['field'];
                unset($this->options['field']);
                if(is_string($fields)) {
                    $fields =   explode(',',$fields);
                }    
            }else{
                $fields =   $this->fields;
            }        
            foreach ($data as $key=>$val){
                if(!in_array($key,$fields,true)){
                    if(!empty($this->options['strict'])){
                        E(L('_DATA_TYPE_INVALID_').':['.$key.'=>'.$val.']');
                    }
                    unset($data[$key]);
                }elseif(is_scalar($val)) {
                    // 字段类型检查 和 强制转换
                    $this->_parseType($data,$key); // 是转换数据类型
                }
            }
        }

        // 安全过滤
        if(!empty($this->options['filter'])) {
            $data = array_map($this->options['filter'],$data); // 数据跑起来了
            unset($this->options['filter']);
        }
        $this->_before_write($data);
        return $data;
     }//总结,就是对数据的 处理
    // 各种数据 处理




























    // 写入数据前的回调方法 包括新增和更新
    protected function _before_write(&$data) {
    }
    // 插入数据前的回调方法
    protected function _before_insert(&$data,$op
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值