ѡ̳

 找回密码
 ע
搜索
查看: 152|回复: 0
打印 上一主题 下一主题

thinkphp model类源码

[复制链接]

789

主题

1158

帖子

4197

积分

Ա

Rank: 9Rank: 9Rank: 9

积分
4197
跳转到指定楼层
¥
发表于 2017-11-12 16:30:59 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式


源码
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think;
  12. /**
  13. * ThinkPHP Model模型类
  14. * 实现了ORM和ActiveRecords模式
  15. */
  16. class Model {
  17.     // 操作状态
  18.     const MODEL_INSERT          =   1;      //  插入模型数据
  19.     const MODEL_UPDATE          =   2;      //  更新模型数据
  20.     const MODEL_BOTH            =   3;      //  包含上面两种方式
  21.     const MUST_VALIDATE         =   1;      // 必须验证
  22.     const EXISTS_VALIDATE       =   0;      // 表单存在字段则验证
  23.     const VALUE_VALIDATE        =   2;      // 表单值不为空则验证

  24.     // 当前数据库操作对象
  25.     protected $db               =   null;
  26.     // 主键名称
  27.     protected $pk               =   'id';
  28.     // 主键是否自动增长
  29.     protected $autoinc          =   false;   
  30.     // 数据表前缀
  31.     protected $tablePrefix      =   null;
  32.     // 模型名称
  33.     protected $name             =   '';
  34.     // 数据库名称
  35.     protected $dbName           =   '';
  36.     //数据库配置
  37.     protected $connection       =   '';
  38.     // 数据表名(不包含表前缀)
  39.     protected $tableName        =   '';
  40.     // 实际数据表名(包含表前缀)
  41.     protected $trueTableName    =   '';
  42.     // 最近错误信息
  43.     protected $error            =   '';
  44.     // 字段信息
  45.     protected $fields           =   array();
  46.     // 数据信息
  47.     protected $data             =   array();
  48.     // 查询表达式参数
  49.     protected $options          =   array();
  50.     protected $_validate        =   array();  // 自动验证定义
  51.     protected $_auto            =   array();  // 自动完成定义
  52.     protected $_map             =   array();  // 字段映射定义
  53.     protected $_scope           =   array();  // 命名范围定义
  54.     // 是否自动检测数据表字段信息
  55.     protected $autoCheckFields  =   true;
  56.     // 是否批处理验证
  57.     protected $patchValidate    =   false;
  58.     // 链操作方法列表
  59.     protected $methods          =   array('order','alias','having','group','lock','distinct','auto','filter','validate','result','token','index');

  60.     /**
  61.      * 架构函数
  62.      * 取得DB类的实例对象 字段检查
  63.      * @access public
  64.      * @param string $name 模型名称
  65.      * @param string $tablePrefix 表前缀
  66.      * @param mixed $connection 数据库连接信息
  67.      */
  68.     public function __construct($name='',$tablePrefix='',$connection='') {
  69.         // 模型初始化
  70.         $this->_initialize();
  71.         // 获取模型名称
  72.         if(!empty($name)) {
  73.             if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
  74.                 list($this->dbName,$this->name) = explode('.',$name);
  75.             }else{
  76.                 $this->name   =  $name;
  77.             }
  78.         }elseif(empty($this->name)){
  79.             $this->name =   $this->getModelName();
  80.         }
  81.         // 设置表前缀
  82.         if(is_null($tablePrefix)) {// 前缀为Null表示没有前缀
  83.             $this->tablePrefix = '';
  84.         }elseif('' != $tablePrefix) {
  85.             $this->tablePrefix = $tablePrefix;
  86.         }elseif(!isset($this->tablePrefix)){
  87.             $this->tablePrefix = C('DB_PREFIX');
  88.         }

  89.         // 数据库初始化操作
  90.         // 获取数据库操作对象
  91.         // 当前模型有独立的数据库连接信息
  92.         $this->db(0,empty($this->connection)?$connection:$this->connection,true);
  93.     }

  94.     /**
  95.      * 自动检测数据表信息
  96.      * @access protected
  97.      * @return void
  98.      */
  99.     protected function _checkTableInfo() {
  100.         // 如果不是Model类 自动记录数据表信息
  101.         // 只在第一次执行记录
  102.         if(empty($this->fields)) {
  103.             // 如果数据表字段没有定义则自动获取
  104.             if(C('DB_FIELDS_CACHE')) {
  105.                 $db   =  $this->dbName?:C('DB_NAME');
  106.                 $fields = F('_fields/'.strtolower($db.'.'.$this->name));
  107.                 if($fields) {
  108.                     $this->fields   =   $fields;
  109.                     $this->pk       =   $fields['_pk'];
  110.                     return ;
  111.                 }
  112.             }
  113.             // 每次都会读取数据表信息
  114.             $this->flush();
  115.         }
  116.     }

  117.     /**
  118.      * 获取字段信息并缓存
  119.      * @access public
  120.      * @return void
  121.      */
  122.     public function flush() {
  123.         // 缓存不存在则查询数据表信息
  124.         $this->db->setModel($this->name);
  125.         $fields =   $this->db->getFields($this->getTableName());
  126.         if(!$fields) { // 无法获取字段信息
  127.             return false;
  128.         }
  129.         $this->fields   =   array_keys($fields);
  130.         foreach ($fields as $key=>$val){
  131.             // 记录字段类型
  132.             $type[$key]     =   $val['type'];
  133.             if($val['primary']) {
  134.                 $this->pk   =   $key;
  135.                 $this->fields['_pk']   =   $key;
  136.                 if($val['autoinc']) $this->autoinc   =   true;
  137.             }
  138.         }
  139.         // 记录字段类型信息
  140.         $this->fields['_type'] =  $type;

  141.         // 2008-3-7 增加缓存开关控制
  142.         if(C('DB_FIELDS_CACHE')){
  143.             // 永久缓存数据表信息
  144.             $db   =  $this->dbName?:C('DB_NAME');
  145.             F('_fields/'.strtolower($db.'.'.$this->name),$this->fields);
  146.         }
  147.     }

  148.     /**
  149.      * 设置数据对象的值
  150.      * @access public
  151.      * @param string $name 名称
  152.      * @param mixed $value 值
  153.      * @return void
  154.      */
  155.     public function __set($name,$value) {
  156.         // 设置数据对象属性
  157.         $this->data[$name]  =   $value;
  158.     }

  159.     /**
  160.      * 获取数据对象的值
  161.      * @access public
  162.      * @param string $name 名称
  163.      * @return mixed
  164.      */
  165.     public function __get($name) {
  166.         return isset($this->data[$name])?$this->data[$name]:null;
  167.     }

  168.     /**
  169.      * 检测数据对象的值
  170.      * @access public
  171.      * @param string $name 名称
  172.      * @return boolean
  173.      */
  174.     public function __isset($name) {
  175.         return isset($this->data[$name]);
  176.     }

  177.     /**
  178.      * 销毁数据对象的值
  179.      * @access public
  180.      * @param string $name 名称
  181.      * @return void
  182.      */
  183.     public function __unset($name) {
  184.         unset($this->data[$name]);
  185.     }

  186.     /**
  187.      * 利用__call方法实现一些特殊的Model方法
  188.      * @access public
  189.      * @param string $method 方法名称
  190.      * @param array $args 调用参数
  191.      * @return mixed
  192.      */
  193.     public function __call($method,$args) {
  194.         if(in_array(strtolower($method),$this->methods,true)) {
  195.             // 连贯操作的实现
  196.             $this->options[strtolower($method)] =   $args[0];
  197.             return $this;
  198.         }elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){
  199.             // 统计查询的实现
  200.             $field =  isset($args[0])?$args[0]:'*';
  201.             return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method);
  202.         }elseif(strtolower(substr($method,0,5))=='getby') {
  203.             // 根据某个字段获取记录
  204.             $field   =   parse_name(substr($method,5));
  205.             $where[$field] =  $args[0];
  206.             return $this->where($where)->find();
  207.         }elseif(strtolower(substr($method,0,10))=='getfieldby') {
  208.             // 根据某个字段获取记录的某个值
  209.             $name   =   parse_name(substr($method,10));
  210.             $where[$name] =$args[0];
  211.             return $this->where($where)->getField($args[1]);
  212.         }elseif(isset($this->_scope[$method])){// 命名范围的单独调用支持
  213.             return $this->scope($method,$args[0]);
  214.         }else{
  215.             E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  216.             return;
  217.         }
  218.     }
  219.     // 回调方法 初始化模型
  220.     protected function _initialize() {}

  221.     /**
  222.      * 对保存到数据库的数据进行处理
  223.      * @access protected
  224.      * @param mixed $data 要操作的数据
  225.      * @return boolean
  226.      */
  227.      protected function _facade($data) {

  228.         // 检查数据字段合法性
  229.         if(!empty($this->fields)) {
  230.             if(!empty($this->options['field'])) {
  231.                 $fields =   $this->options['field'];
  232.                 unset($this->options['field']);
  233.                 if(is_string($fields)) {
  234.                     $fields =   explode(',',$fields);
  235.                 }   
  236.             }else{
  237.                 $fields =   $this->fields;
  238.             }        
  239.             foreach ($data as $key=>$val){
  240.                 if(!in_array($key,$fields,true)){
  241.                     if(APP_DEBUG){
  242.                         E(L('_DATA_TYPE_INVALID_').':['.$key.'=>'.$val.']');
  243.                     }                    
  244.                     unset($data[$key]);
  245.                 }elseif(is_scalar($val)) {
  246.                     // 字段类型检查 和 强制转换
  247.                     $this->_parseType($data,$key);
  248.                 }
  249.             }
  250.         }
  251.       
  252.         // 安全过滤
  253.         if(!empty($this->options['filter'])) {
  254.             $data = array_map($this->options['filter'],$data);
  255.             unset($this->options['filter']);
  256.         }
  257.         $this->_before_write($data);
  258.         return $data;
  259.      }

  260.     // 写入数据前的回调方法 包括新增和更新
  261.     protected function _before_write(&$data) {}

  262.     /**
  263.      * 新增数据
  264.      * @access public
  265.      * @param mixed $data 数据
  266.      * @param array $options 表达式
  267.      * @param boolean $replace 是否replace
  268.      * @return mixed
  269.      */
  270.     public function add($data='',$options=array(),$replace=false) {
  271.         if(empty($data)) {
  272.             // 没有传递数据,获取当前数据对象的值
  273.             if(!empty($this->data)) {
  274.                 $data           =   $this->data;
  275.                 // 重置数据
  276.                 $this->data     = array();
  277.             }else{
  278.                 $this->error    = L('_DATA_TYPE_INVALID_');
  279.                 return false;
  280.             }
  281.         }
  282.         // 分析表达式
  283.         $options    =   $this->_parseOptions($options);
  284.         // 数据处理
  285.         $data       =   $this->_facade($data);
  286.         if(false === $this->_before_insert($data,$options)) {
  287.             return false;
  288.         }
  289.         // 写入数据到数据库
  290.         $result = $this->db->insert($data,$options,$replace);
  291.         if(false !== $result ) {
  292.             $insertId   =   $this->getLastInsID();
  293.             if($insertId) {
  294.                 // 自增主键返回插入ID
  295.                 $data[$this->getPk()]  = $insertId;
  296.                 if(false === $this->_after_insert($data,$options)) {
  297.                     return false;
  298.                 }
  299.                 return $insertId;
  300.             }
  301.             if(false === $this->_after_insert($data,$options)) {
  302.                 return false;
  303.             }
  304.         }
  305.         return $result;
  306.     }
  307.     // 插入数据前的回调方法
  308.     protected function _before_insert(&$data,$options) {}
  309.     // 插入成功后的回调方法
  310.     protected function _after_insert($data,$options) {}

  311.     public function addAll($dataList,$options=array(),$replace=false){
  312.         if(empty($dataList)) {
  313.             $this->error = L('_DATA_TYPE_INVALID_');
  314.             return false;
  315.         }
  316.         // 分析表达式
  317.         $options =  $this->_parseOptions($options);
  318.         // 数据处理
  319.         foreach ($dataList as $key=>$data){
  320.             $dataList[$key] = $this->_facade($data);
  321.         }
  322.         // 写入数据到数据库
  323.         $result = $this->db->insertAll($dataList,$options,$replace);
  324.         if(false !== $result ) {
  325.             $insertId   =   $this->getLastInsID();
  326.             if($insertId) {
  327.                 return $insertId;
  328.             }
  329.         }
  330.         return $result;
  331.     }

  332.     /**
  333.      * 通过Select方式添加记录
  334.      * @access public
  335.      * @param string $fields 要插入的数据表字段名
  336.      * @param string $table 要插入的数据表名
  337.      * @param array $options 表达式
  338.      * @return boolean
  339.      */
  340.     public function selectAdd($fields='',$table='',$options=array()) {
  341.         // 分析表达式
  342.         $options =  $this->_parseOptions($options);
  343.         // 写入数据到数据库
  344.         if(false === $result = $this->db->selectInsert($fields?:$options['field'],$table?:$this->getTableName(),$options)){
  345.             // 数据库插入操作失败
  346.             $this->error = L('_OPERATION_WRONG_');
  347.             return false;
  348.         }else {
  349.             // 插入成功
  350.             return $result;
  351.         }
  352.     }

  353.     /**
  354.      * 保存数据
  355.      * @access public
  356.      * @param mixed $data 数据
  357.      * @param array $options 表达式
  358.      * @return boolean
  359.      */
  360.     public function save($data='',$options=array()) {
  361.         if(empty($data)) {
  362.             // 没有传递数据,获取当前数据对象的值
  363.             if(!empty($this->data)) {
  364.                 $data           =   $this->data;
  365.                 // 重置数据
  366.                 $this->data     =   array();
  367.             }else{
  368.                 $this->error    =   L('_DATA_TYPE_INVALID_');
  369.                 return false;
  370.             }
  371.         }
  372.         // 数据处理
  373.         $data       =   $this->_facade($data);
  374.         if(empty($data)){
  375.             // 没有数据则不执行
  376.             $this->error    =   L('_DATA_TYPE_INVALID_');
  377.             return false;
  378.         }
  379.         // 分析表达式
  380.         $options    =   $this->_parseOptions($options);
  381.         $pk         =   $this->getPk();
  382.         if(!isset($options['where']) ) {
  383.             // 如果存在主键数据 则自动作为更新条件
  384.             if(isset($data[$pk])) {
  385.                 $where[$pk]         =   $data[$pk];
  386.                 $options['where']   =   $where;
  387.                 unset($data[$pk]);
  388.             }else{
  389.                 // 如果没有任何更新条件则不执行
  390.                 $this->error        =   L('_OPERATION_WRONG_');
  391.                 return false;
  392.             }
  393.         }
  394.         if(is_array($options['where']) && isset($options['where'][$pk])){
  395.             $pkValue    =   $options['where'][$pk];
  396.         }        
  397.         if(false === $this->_before_update($data,$options)) {
  398.             return false;
  399.         }        
  400.         $result     =   $this->db->update($data,$options);
  401.         if(false !== $result) {
  402.             if(isset($pkValue)) $data[$pk]   =  $pkValue;
  403.             $this->_after_update($data,$options);
  404.         }
  405.         return $result;
  406.     }
  407.     // 更新数据前的回调方法
  408.     protected function _before_update(&$data,$options) {}
  409.     // 更新成功后的回调方法
  410.     protected function _after_update($data,$options) {}

  411.     /**
  412.      * 删除数据
  413.      * @access public
  414.      * @param mixed $options 表达式
  415.      * @return mixed
  416.      */
  417.     public function delete($options=array()) {
  418.         if(empty($options) && empty($this->options['where'])) {
  419.             // 如果删除条件为空 则删除当前数据对象所对应的记录
  420.             if(!empty($this->data) && isset($this->data[$this->getPk()]))
  421.                 return $this->delete($this->data[$this->getPk()]);
  422.             else
  423.                 return false;
  424.         }
  425.         $pk   =  $this->getPk();
  426.         if(is_numeric($options)  || is_string($options)) {
  427.             // 根据主键删除记录
  428.             if(strpos($options,',')) {
  429.                 $where[$pk]     =  array('IN', $options);
  430.             }else{
  431.                 $where[$pk]     =  $options;
  432.             }
  433.             $options            =  array();
  434.             $options['where']   =  $where;
  435.         }
  436.         // 分析表达式
  437.         $options =  $this->_parseOptions($options);
  438.         if(empty($options['where'])){
  439.             // 如果条件为空 不进行删除操作 除非设置 1=1
  440.             return false;
  441.         }        
  442.         if(is_array($options['where']) && isset($options['where'][$pk])){
  443.             $pkValue            =  $options['where'][$pk];
  444.         }

  445.         if(false === $this->_before_delete($options)) {
  446.             return false;
  447.         }        
  448.         $result  =    $this->db->delete($options);
  449.         if(false !== $result) {
  450.             $data = array();
  451.             if(isset($pkValue)) $data[$pk]   =  $pkValue;
  452.             $this->_after_delete($data,$options);
  453.         }
  454.         // 返回删除记录个数
  455.         return $result;
  456.     }
  457.     // 删除数据前的回调方法
  458.     protected function _before_delete($options) {}   
  459.     // 删除成功后的回调方法
  460.     protected function _after_delete($data,$options) {}

  461.     /**
  462.      * 查询数据集
  463.      * @access public
  464.      * @param array $options 表达式参数
  465.      * @return mixed
  466.      */
  467.     public function select($options=array()) {
  468.         if(is_string($options) || is_numeric($options)) {
  469.             // 根据主键查询
  470.             $pk   =  $this->getPk();
  471.             if(strpos($options,',')) {
  472.                 $where[$pk]     =  array('IN',$options);
  473.             }else{
  474.                 $where[$pk]     =  $options;
  475.             }
  476.             $options            =  array();
  477.             $options['where']   =  $where;
  478.         }elseif(false === $options){ // 用于子查询 不查询只返回SQL
  479.             $options            =  array();
  480.             // 分析表达式
  481.             $options            =  $this->_parseOptions($options);
  482.             return  '( '.$this->db->buildSelectSql($options).' )';
  483.         }
  484.         // 分析表达式
  485.         $options    =  $this->_parseOptions($options);
  486.         // 判断查询缓存
  487.         if(isset($options['cache'])){
  488.             $cache  =   $options['cache'];
  489.             $key    =   is_string($cache['key'])?$cache['key']:md5(serialize($options));
  490.             $data   =   S($key,'',$cache);
  491.             if(false !== $data){
  492.                 return $data;
  493.             }
  494.         }        
  495.         $resultSet  = $this->db->select($options);
  496.         if(false === $resultSet) {
  497.             return false;
  498.         }
  499.         if(empty($resultSet)) { // 查询结果为空
  500.             return null;
  501.         }
  502.         $resultSet  =   array_map(array($this,'_read_data'),$resultSet);
  503.         $this->_after_select($resultSet,$options);
  504.         if(isset($options['index'])){ // 对数据集进行索引
  505.             $index  =   explode(',',$options['index']);
  506.             foreach ($resultSet as $result){
  507.                 $_key   =  $result[$index[0]];
  508.                 if(isset($index[1]) && isset($result[$index[1]])){
  509.                     $cols[$_key] =  $result[$index[1]];
  510.                 }else{
  511.                     $cols[$_key] =  $result;
  512.                 }
  513.             }
  514.             $resultSet  =   $cols;         
  515.         }
  516.         if(isset($cache)){
  517.             S($key,$resultSet,$cache);
  518.         }           
  519.         return $resultSet;
  520.     }
  521.     // 查询成功后的回调方法
  522.     protected function _after_select(&$resultSet,$options) {}

  523.     /**
  524.      * 生成查询SQL 可用于子查询
  525.      * @access public
  526.      * @param array $options 表达式参数
  527.      * @return string
  528.      */
  529.     public function buildSql($options=array()) {
  530.         // 分析表达式
  531.         $options =  $this->_parseOptions($options);
  532.         return  '( '.$this->db->buildSelectSql($options).' )';
  533.     }

  534.     /**
  535.      * 分析表达式
  536.      * @access protected
  537.      * @param array $options 表达式参数
  538.      * @return array
  539.      */
  540.     protected function _parseOptions($options=array()) {
  541.         if(is_array($options))
  542.             $options =  array_merge($this->options,$options);

  543.         if(!isset($options['table'])){
  544.             // 自动获取表名
  545.             $options['table']   =   $this->getTableName();
  546.             $fields             =   $this->fields;
  547.         }else{
  548.             // 指定数据表 则重新获取字段列表 但不支持类型检测
  549.             $fields             =   $this->getDbFields();
  550.         }

  551.         // 数据表别名
  552.         if(!empty($options['alias'])) {
  553.             $options['table']  .=   ' '.$options['alias'];
  554.         }
  555.         // 记录操作的模型名称
  556.         $options['model']       =   $this->name;

  557.         // 字段类型验证
  558.         if(isset($options['where']) && is_array($options['where']) && !empty($fields) && !isset($options['join'])) {
  559.             // 对数组查询条件进行字段类型检查
  560.             foreach ($options['where'] as $key=>$val){
  561.                 $key            =   trim($key);
  562.                 if(in_array($key,$fields,true)){
  563.                     if(is_scalar($val)) {
  564.                         $this->_parseType($options['where'],$key);
  565.                     }
  566.                 }elseif(!is_numeric($key) && '_' != substr($key,0,1) && false === strpos($key,'.') && false === strpos($key,'(') && false === strpos($key,'|') && false === strpos($key,'&')){
  567.                     if(APP_DEBUG){
  568.                         E(L('_ERROR_QUERY_EXPRESS_').':['.$key.'=>'.$val.']');
  569.                     }
  570.                     unset($options['where'][$key]);
  571.                 }
  572.             }
  573.         }
  574.         // 查询过后清空sql表达式组装 避免影响下次查询
  575.         $this->options  =   array();
  576.         // 表达式过滤
  577.         $this->_options_filter($options);
  578.         return $options;
  579.     }
  580.     // 表达式过滤回调方法
  581.     protected function _options_filter(&$options) {}

  582.     /**
  583.      * 数据类型检测
  584.      * @access protected
  585.      * @param mixed $data 数据
  586.      * @param string $key 字段名
  587.      * @return void
  588.      */
  589.     protected function _parseType(&$data,$key) {
  590.         if(!isset($this->options['bind'][':'.$key]) && isset($this->fields['_type'][$key])){
  591.             $fieldType = strtolower($this->fields['_type'][$key]);
  592.             if(false !== strpos($fieldType,'enum')){
  593.                 // 支持ENUM类型优先检测
  594.             }elseif(false === strpos($fieldType,'bigint') && false !== strpos($fieldType,'int')) {
  595.                 $data[$key]   =  intval($data[$key]);
  596.             }elseif(false !== strpos($fieldType,'float') || false !== strpos($fieldType,'double')){
  597.                 $data[$key]   =  floatval($data[$key]);
  598.             }elseif(false !== strpos($fieldType,'bool')){
  599.                 $data[$key]   =  (bool)$data[$key];
  600.             }
  601.         }
  602.     }

  603.     /**
  604.      * 数据读取后的处理
  605.      * @access protected
  606.      * @param array $data 当前数据
  607.      * @return array
  608.      */
  609.     protected function _read_data($data) {
  610.         // 检查字段映射
  611.         if(!empty($this->_map) && C('READ_DATA_MAP')) {
  612.             foreach ($this->_map as $key=>$val){
  613.                 if(isset($data[$val])) {
  614.                     $data[$key] =   $data[$val];
  615.                     unset($data[$val]);
  616.                 }
  617.             }
  618.         }
  619.         return $data;
  620.     }

  621.     /**
  622.      * 查询数据
  623.      * @access public
  624.      * @param mixed $options 表达式参数
  625.      * @return mixed
  626.      */
  627.     public function find($options=array()) {
  628.         if(is_numeric($options) || is_string($options)) {
  629.             $where[$this->getPk()]  =   $options;
  630.             $options                =   array();
  631.             $options['where']       =   $where;
  632.         }
  633.         // 总是查找一条记录
  634.         $options['limit']   =   1;
  635.         // 分析表达式
  636.         $options            =   $this->_parseOptions($options);
  637.         // 判断查询缓存
  638.         if(isset($options['cache'])){
  639.             $cache  =   $options['cache'];
  640.             $key    =   is_string($cache['key'])?$cache['key']:md5(serialize($options));
  641.             $data   =   S($key,'',$cache);
  642.             if(false !== $data){
  643.                 $this->data     =   $data;
  644.                 return $data;
  645.             }
  646.         }
  647.         $resultSet          =   $this->db->select($options);
  648.         if(false === $resultSet) {
  649.             return false;
  650.         }
  651.         if(empty($resultSet)) {// 查询结果为空
  652.             return null;
  653.         }
  654.         // 读取数据后的处理
  655.         $data   =   $this->_read_data($resultSet[0]);
  656.         $this->_after_find($data,$options);
  657.         if(!empty($this->options['result'])) {
  658.             return $this->returnResult($data,$this->options['result']);
  659.         }
  660.         $this->data     =   $data;
  661.         if(isset($cache)){
  662.             S($key,$data,$cache);
  663.         }
  664.         return $this->data;
  665.     }
  666.     // 查询成功的回调方法
  667.     protected function _after_find(&$result,$options) {}

  668.     protected function returnResult($data,$type=''){
  669.         if ($type){
  670.             if(is_callable($type)){
  671.                 return call_user_func($type,$data);
  672.             }
  673.             switch (strtolower($type)){
  674.                 case 'json':
  675.                     return json_encode($data);
  676.                 case 'xml':
  677.                     return xml_encode($data);
  678.             }
  679.         }
  680.         return $data;
  681.     }

  682.     /**
  683.      * 处理字段映射
  684.      * @access public
  685.      * @param array $data 当前数据
  686.      * @param integer $type 类型 0 写入 1 读取
  687.      * @return array
  688.      */
  689.     public function parseFieldsMap($data,$type=1) {
  690.         // 检查字段映射
  691.         if(!empty($this->_map)) {
  692.             foreach ($this->_map as $key=>$val){
  693.                 if($type==1) { // 读取
  694.                     if(isset($data[$val])) {
  695.                         $data[$key] =   $data[$val];
  696.                         unset($data[$val]);
  697.                     }
  698.                 }else{
  699.                     if(isset($data[$key])) {
  700.                         $data[$val] =   $data[$key];
  701.                         unset($data[$key]);
  702.                     }
  703.                 }
  704.             }
  705.         }
  706.         return $data;
  707.     }

  708.     /**
  709.      * 设置记录的某个字段值
  710.      * 支持使用数据库字段和方法
  711.      * @access public
  712.      * @param string|array $field  字段名
  713.      * @param string $value  字段值
  714.      * @return boolean
  715.      */
  716.     public function setField($field,$value='') {
  717.         if(is_array($field)) {
  718.             $data           =   $field;
  719.         }else{
  720.             $data[$field]   =   $value;
  721.         }
  722.         return $this->save($data);
  723.     }

  724.     /**
  725.      * 字段值增长
  726.      * @access public
  727.      * @param string $field  字段名
  728.      * @param integer $step  增长值
  729.      * @return boolean
  730.      */
  731.     public function setInc($field,$step=1) {
  732.         return $this->setField($field,array('exp',$field.'+'.$step));
  733.     }

  734.     /**
  735.      * 字段值减少
  736.      * @access public
  737.      * @param string $field  字段名
  738.      * @param integer $step  减少值
  739.      * @return boolean
  740.      */
  741.     public function setDec($field,$step=1) {
  742.         return $this->setField($field,array('exp',$field.'-'.$step));
  743.     }

  744.     /**
  745.      * 获取一条记录的某个字段值
  746.      * @access public
  747.      * @param string $field  字段名
  748.      * @param string $spea  字段数据间隔符号 NULL返回数组
  749.      * @return mixed
  750.      */
  751.     public function getField($field,$sepa=null) {
  752.         $options['field']       =   $field;
  753.         $options                =   $this->_parseOptions($options);
  754.         // 判断查询缓存
  755.         if(isset($options['cache'])){
  756.             $cache  =   $options['cache'];
  757.             $key    =   is_string($cache['key'])?$cache['key']:md5($sepa.serialize($options));
  758.             $data   =   S($key,'',$cache);
  759.             if(false !== $data){
  760.                 return $data;
  761.             }
  762.         }        
  763.         $field                  =   trim($field);
  764.         if(strpos($field,',') && false !== $sepa) { // 多字段
  765.             if(!isset($options['limit'])){
  766.                 $options['limit']   =   is_numeric($sepa)?$sepa:'';
  767.             }
  768.             $resultSet          =   $this->db->select($options);
  769.             if(!empty($resultSet)) {
  770.                 $_field         =   explode(',', $field);
  771.                 $field          =   array_keys($resultSet[0]);
  772.                 $key            =   array_shift($field);
  773.                 $key2           =   array_shift($field);
  774.                 $cols           =   array();
  775.                 $count          =   count($_field);
  776.                 foreach ($resultSet as $result){
  777.                     $name   =  $result[$key];
  778.                     if(2==$count) {
  779.                         $cols[$name]   =  $result[$key2];
  780.                     }else{
  781.                         $cols[$name]   =  is_string($sepa)?implode($sepa,array_slice($result,1)):$result;
  782.                     }
  783.                 }
  784.                 if(isset($cache)){
  785.                     S($key,$cols,$cache);
  786.                 }
  787.                 return $cols;
  788.             }
  789.         }else{   // 查找一条记录
  790.             // 返回数据个数
  791.             if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
  792.                 $options['limit']   =   is_numeric($sepa)?$sepa:1;
  793.             }
  794.             $result = $this->db->select($options);
  795.             if(!empty($result)) {
  796.                 if(true !== $sepa && 1==$options['limit']) {
  797.                     $data   =   reset($result[0]);
  798.                     if(isset($cache)){
  799.                         S($key,$data,$cache);
  800.                     }            
  801.                     return $data;
  802.                 }
  803.                 foreach ($result as $val){
  804.                     $array[]    =   $val[$field];
  805.                 }
  806.                 if(isset($cache)){
  807.                     S($key,$array,$cache);
  808.                 }               
  809.                 return $array;
  810.             }
  811.         }
  812.         return null;
  813.     }

  814.     /**
  815.      * 创建数据对象 但不保存到数据库
  816.      * @access public
  817.      * @param mixed $data 创建数据
  818.      * @param string $type 状态
  819.      * @return mixed
  820.      */
  821.      public function create($data='',$type='') {
  822.         // 如果没有传值默认取POST数据
  823.         if(empty($data)) {
  824.             $data   =   I('post.');
  825.         }elseif(is_object($data)){
  826.             $data   =   get_object_vars($data);
  827.         }
  828.         // 验证数据
  829.         if(empty($data) || !is_array($data)) {
  830.             $this->error = L('_DATA_TYPE_INVALID_');
  831.             return false;
  832.         }

  833.         // 状态
  834.         $type = $type?:(!empty($data[$this->getPk()])?self::MODEL_UPDATE:self::MODEL_INSERT);

  835.         // 检查字段映射
  836.         if(!empty($this->_map)) {
  837.             foreach ($this->_map as $key=>$val){
  838.                 if(isset($data[$key])) {
  839.                     $data[$val] =   $data[$key];
  840.                     unset($data[$key]);
  841.                 }
  842.             }
  843.         }

  844.         // 检测提交字段的合法性
  845.         if(isset($this->options['field'])) { // $this->field('field1,field2...')->create()
  846.             $fields =   $this->options['field'];
  847.             unset($this->options['field']);
  848.         }elseif($type == self::MODEL_INSERT && isset($this->insertFields)) {
  849.             $fields =   $this->insertFields;
  850.         }elseif($type == self::MODEL_UPDATE && isset($this->updateFields)) {
  851.             $fields =   $this->updateFields;
  852.         }
  853.         if(isset($fields)) {
  854.             if(is_string($fields)) {
  855.                 $fields =   explode(',',$fields);
  856.             }
  857.             // 判断令牌验证字段
  858.             if(C('TOKEN_ON'))   $fields[] = C('TOKEN_NAME');
  859.             foreach ($data as $key=>$val){
  860.                 if(!in_array($key,$fields)) {
  861.                     unset($data[$key]);
  862.                 }
  863.             }
  864.         }

  865.         // 数据自动验证
  866.         if(!$this->autoValidation($data,$type)) return false;

  867.         // 表单令牌验证
  868.         if(!$this->autoCheckToken($data)) {
  869.             $this->error = L('_TOKEN_ERROR_');
  870.             return false;
  871.         }

  872.         // 验证完成生成数据对象
  873.         if($this->autoCheckFields) { // 开启字段检测 则过滤非法字段数据
  874.             $fields =   $this->getDbFields();
  875.             foreach ($data as $key=>$val){
  876.                 if(!in_array($key,$fields)) {
  877.                     unset($data[$key]);
  878.                 }elseif(MAGIC_QUOTES_GPC && is_string($val)){
  879.                     $data[$key] =   stripslashes($val);
  880.                 }
  881.             }
  882.         }

  883.         // 创建完成对数据进行自动处理
  884.         $this->autoOperation($data,$type);
  885.         // 赋值当前数据对象
  886.         $this->data =   $data;
  887.         // 返回创建的数据以供其他调用
  888.         return $data;
  889.      }

  890.     // 自动表单令牌验证
  891.     // TODO  ajax无刷新多次提交暂不能满足
  892.     public function autoCheckToken($data) {
  893.         // 支持使用token(false) 关闭令牌验证
  894.         if(isset($this->options['token']) && !$this->options['token']) return true;
  895.         if(C('TOKEN_ON')){
  896.             $name   = C('TOKEN_NAME', null, '__hash__');
  897.             if(!isset($data[$name]) || !isset($_SESSION[$name])) { // 令牌数据无效
  898.                 return false;
  899.             }

  900.             // 令牌验证
  901.             list($key,$value)  =  explode('_',$data[$name]);
  902.             if($value && $_SESSION[$name][$key] === $value) { // 防止重复提交
  903.                 unset($_SESSION[$name][$key]); // 验证完成销毁session
  904.                 return true;
  905.             }
  906.             // 开启TOKEN重置
  907.             if(C('TOKEN_RESET')) unset($_SESSION[$name][$key]);
  908.             return false;
  909.         }
  910.         return true;
  911.     }

  912.     /**
  913.      * 使用正则验证数据
  914.      * @access public
  915.      * @param string $value  要验证的数据
  916.      * @param string $rule 验证规则
  917.      * @return boolean
  918.      */
  919.     public function regex($value,$rule) {
  920.         $validate = array(
  921.             'require'   =>  '/\S+/',
  922.             'email'     =>  '/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/',
  923.             'url'       =>  '/^http(s?):\/\/(?:[A-za-z0-9-]+\.)+[A-za-z]{2,4}(?:[\/\?#][\/=\?%\-&~`@[\]\':+!\.#\w]*)?$/',
  924.             'currency'  =>  '/^\d+(\.\d+)?$/',
  925.             'number'    =>  '/^\d+$/',
  926.             'zip'       =>  '/^\d{6}$/',
  927.             'integer'   =>  '/^[-\+]?\d+$/',
  928.             'double'    =>  '/^[-\+]?\d+(\.\d+)?$/',
  929.             'english'   =>  '/^[A-Za-z]+$/',
  930.         );
  931.         // 检查是否有内置的正则表达式
  932.         if(isset($validate[strtolower($rule)]))
  933.             $rule       =   $validate[strtolower($rule)];
  934.         return preg_match($rule,$value)===1;
  935.     }

  936.     /**
  937.      * 自动表单处理
  938.      * @access public
  939.      * @param array $data 创建数据
  940.      * @param string $type 创建类型
  941.      * @return mixed
  942.      */
  943.     private function autoOperation(&$data,$type) {
  944.         if(!empty($this->options['auto'])) {
  945.             $_auto   =   $this->options['auto'];
  946.             unset($this->options['auto']);
  947.         }elseif(!empty($this->_auto)){
  948.             $_auto   =   $this->_auto;
  949.         }
  950.         // 自动填充
  951.         if(isset($_auto)) {
  952.             foreach ($_auto as $auto){
  953.                 // 填充因子定义格式
  954.                 // array('field','填充内容','填充条件','附加规则',[额外参数])
  955.                 if(empty($auto[2])) $auto[2] =  self::MODEL_INSERT; // 默认为新增的时候自动填充
  956.                 if( $type == $auto[2] || $auto[2] == self::MODEL_BOTH) {
  957.                     if(empty($auto[3])) $auto[3] =  'string';
  958.                     switch(trim($auto[3])) {
  959.                         case 'function':    //  使用函数进行填充 字段的值作为参数
  960.                         case 'callback': // 使用回调方法
  961.                             $args = isset($auto[4])?(array)$auto[4]:array();
  962.                             if(isset($data[$auto[0]])) {
  963.                                 array_unshift($args,$data[$auto[0]]);
  964.                             }
  965.                             if('function'==$auto[3]) {
  966.                                 $data[$auto[0]]  = call_user_func_array($auto[1], $args);
  967.                             }else{
  968.                                 $data[$auto[0]]  =  call_user_func_array(array(&$this,$auto[1]), $args);
  969.                             }
  970.                             break;
  971.                         case 'field':    // 用其它字段的值进行填充
  972.                             $data[$auto[0]] = $data[$auto[1]];
  973.                             break;
  974.                         case 'ignore': // 为空忽略
  975.                             if($auto[1]===$data[$auto[0]])
  976.                                 unset($data[$auto[0]]);
  977.                             break;
  978.                         case 'string':
  979.                         default: // 默认作为字符串填充
  980.                             $data[$auto[0]] = $auto[1];
  981.                     }
  982.                     if(isset($data[$auto[0]]) && false === $data[$auto[0]] )   unset($data[$auto[0]]);
  983.                 }
  984.             }
  985.         }
  986.         return $data;
  987.     }

  988.     /**
  989.      * 自动表单验证
  990.      * @access protected
  991.      * @param array $data 创建数据
  992.      * @param string $type 创建类型
  993.      * @return boolean
  994.      */
  995.     protected function autoValidation($data,$type) {
  996.         if(!empty($this->options['validate'])) {
  997.             $_validate   =   $this->options['validate'];
  998.             unset($this->options['validate']);
  999.         }elseif(!empty($this->_validate)){
  1000.             $_validate   =   $this->_validate;
  1001.         }
  1002.         // 属性验证
  1003.         if(isset($_validate)) { // 如果设置了数据自动验证则进行数据验证
  1004.             if($this->patchValidate) { // 重置验证错误信息
  1005.                 $this->error = array();
  1006.             }
  1007.             foreach($_validate as $key=>$val) {
  1008.                 // 验证因子定义格式
  1009.                 // array(field,rule,message,condition,type,when,params)
  1010.                 // 判断是否需要执行验证
  1011.                 if(empty($val[5]) || $val[5]== self::MODEL_BOTH || $val[5]== $type ) {
  1012.                     if(0==strpos($val[2],'{%') && strpos($val[2],'}'))
  1013.                         // 支持提示信息的多语言 使用 {%语言定义} 方式
  1014.                         $val[2]  =  L(substr($val[2],2,-1));
  1015.                     $val[3]  =  isset($val[3])?$val[3]:self::EXISTS_VALIDATE;
  1016.                     $val[4]  =  isset($val[4])?$val[4]:'regex';
  1017.                     // 判断验证条件
  1018.                     switch($val[3]) {
  1019.                         case self::MUST_VALIDATE:   // 必须验证 不管表单是否有设置该字段
  1020.                             if(false === $this->_validationField($data,$val))
  1021.                                 return false;
  1022.                             break;
  1023.                         case self::VALUE_VALIDATE:    // 值不为空的时候才验证
  1024.                             if('' != trim($data[$val[0]]))
  1025.                                 if(false === $this->_validationField($data,$val))
  1026.                                     return false;
  1027.                             break;
  1028.                         default:    // 默认表单存在该字段就验证
  1029.                             if(isset($data[$val[0]]))
  1030.                                 if(false === $this->_validationField($data,$val))
  1031.                                     return false;
  1032.                     }
  1033.                 }
  1034.             }
  1035.             // 批量验证的时候最后返回错误
  1036.             if(!empty($this->error)) return false;
  1037.         }
  1038.         return true;
  1039.     }

  1040.     /**
  1041.      * 验证表单字段 支持批量验证
  1042.      * 如果批量验证返回错误的数组信息
  1043.      * @access protected
  1044.      * @param array $data 创建数据
  1045.      * @param array $val 验证因子
  1046.      * @return boolean
  1047.      */
  1048.     protected function _validationField($data,$val) {
  1049.         if($this->patchValidate && isset($this->error[$val[0]]))
  1050.             return ; //当前字段已经有规则验证没有通过
  1051.         if(false === $this->_validationFieldItem($data,$val)){
  1052.             if($this->patchValidate) {
  1053.                 $this->error[$val[0]]   =   $val[2];
  1054.             }else{
  1055.                 $this->error            =   $val[2];
  1056.                 return false;
  1057.             }
  1058.         }
  1059.         return ;
  1060.     }

  1061.     /**
  1062.      * 根据验证因子验证字段
  1063.      * @access protected
  1064.      * @param array $data 创建数据
  1065.      * @param array $val 验证因子
  1066.      * @return boolean
  1067.      */
  1068.     protected function _validationFieldItem($data,$val) {
  1069.         switch(strtolower(trim($val[4]))) {
  1070.             case 'function':// 使用函数进行验证
  1071.             case 'callback':// 调用方法进行验证
  1072.                 $args = isset($val[6])?(array)$val[6]:array();
  1073.                 if(is_string($val[0]) && strpos($val[0], ','))
  1074.                     $val[0] = explode(',', $val[0]);
  1075.                 if(is_array($val[0])){
  1076.                     // 支持多个字段验证
  1077.                     foreach($val[0] as $field)
  1078.                         $_data[$field] = $data[$field];
  1079.                     array_unshift($args, $_data);
  1080.                 }else{
  1081.                     array_unshift($args, $data[$val[0]]);
  1082.                 }
  1083.                 if('function'==$val[4]) {
  1084.                     return call_user_func_array($val[1], $args);
  1085.                 }else{
  1086.                     return call_user_func_array(array(&$this, $val[1]), $args);
  1087.                 }
  1088.             case 'confirm': // 验证两个字段是否相同
  1089.                 return $data[$val[0]] == $data[$val[1]];
  1090.             case 'unique': // 验证某个值是否唯一
  1091.                 if(is_string($val[0]) && strpos($val[0],','))
  1092.                     $val[0]  =  explode(',',$val[0]);
  1093.                 $map = array();
  1094.                 if(is_array($val[0])) {
  1095.                     // 支持多个字段验证
  1096.                     foreach ($val[0] as $field)
  1097.                         $map[$field]   =  $data[$field];
  1098.                 }else{
  1099.                     $map[$val[0]] = $data[$val[0]];
  1100.                 }
  1101.                 if(!empty($data[$this->getPk()])) { // 完善编辑的时候验证唯一
  1102.                     $map[$this->getPk()] = array('neq',$data[$this->getPk()]);
  1103.                 }
  1104.                 if($this->where($map)->find())   return false;
  1105.                 return true;
  1106.             default:  // 检查附加规则
  1107.                 return $this->check($data[$val[0]],$val[1],$val[4]);
  1108.         }
  1109.     }

  1110.     /**
  1111.      * 验证数据 支持 in between equal length regex expire ip_allow ip_deny
  1112.      * @access public
  1113.      * @param string $value 验证数据
  1114.      * @param mixed $rule 验证表达式
  1115.      * @param string $type 验证方式 默认为正则验证
  1116.      * @return boolean
  1117.      */
  1118.     public function check($value,$rule,$type='regex'){
  1119.         $type   =   strtolower(trim($type));
  1120.         switch($type) {
  1121.             case 'in': // 验证是否在某个指定范围之内 逗号分隔字符串或者数组
  1122.             case 'notin':
  1123.                 $range   = is_array($rule)? $rule : explode(',',$rule);
  1124.                 return $type == 'in' ? in_array($value ,$range) : !in_array($value ,$range);
  1125.             case 'between': // 验证是否在某个范围
  1126.             case 'notbetween': // 验证是否不在某个范围            
  1127.                 if (is_array($rule)){
  1128.                     $min    =    $rule[0];
  1129.                     $max    =    $rule[1];
  1130.                 }else{
  1131.                     list($min,$max)   =  explode(',',$rule);
  1132.                 }
  1133.                 return $type == 'between' ? $value>=$min && $value<=$max : $value<$min || $value>$max;
  1134.             case 'equal': // 验证是否等于某个值
  1135.             case 'notequal': // 验证是否等于某个值            
  1136.                 return $type == 'equal' ? $value == $rule : $value != $rule;
  1137.             case 'length': // 验证长度
  1138.                 $length  =  mb_strlen($value,'utf-8'); // 当前数据长度
  1139.                 if(strpos($rule,',')) { // 长度区间
  1140.                     list($min,$max)   =  explode(',',$rule);
  1141.                     return $length >= $min && $length <= $max;
  1142.                 }else{// 指定长度
  1143.                     return $length == $rule;
  1144.                 }
  1145.             case 'expire':
  1146.                 list($start,$end)   =  explode(',',$rule);
  1147.                 if(!is_numeric($start)) $start   =  strtotime($start);
  1148.                 if(!is_numeric($end)) $end   =  strtotime($end);
  1149.                 return NOW_TIME >= $start && NOW_TIME <= $end;
  1150.             case 'ip_allow': // IP 操作许可验证
  1151.                 return in_array(get_client_ip(),explode(',',$rule));
  1152.             case 'ip_deny': // IP 操作禁止验证
  1153.                 return !in_array(get_client_ip(),explode(',',$rule));
  1154.             case 'regex':
  1155.             default:    // 默认使用正则验证 可以使用验证类中定义的验证名称
  1156.                 // 检查附加规则
  1157.                 return $this->regex($value,$rule);
  1158.         }
  1159.     }

  1160.     /**
  1161.      * SQL查询
  1162.      * @access public
  1163.      * @param string $sql  SQL指令
  1164.      * @param mixed $parse  是否需要解析SQL
  1165.      * @return mixed
  1166.      */
  1167.     public function query($sql,$parse=false) {
  1168.         if(!is_bool($parse) && !is_array($parse)) {
  1169.             $parse = func_get_args();
  1170.             array_shift($parse);
  1171.         }
  1172.         $sql  =   $this->parseSql($sql,$parse);
  1173.         return $this->db->query($sql);
  1174.     }

  1175.     /**
  1176.      * 执行SQL语句
  1177.      * @access public
  1178.      * @param string $sql  SQL指令
  1179.      * @param mixed $parse  是否需要解析SQL
  1180.      * @return false | integer
  1181.      */
  1182.     public function execute($sql,$parse=false) {
  1183.         if(!is_bool($parse) && !is_array($parse)) {
  1184.             $parse = func_get_args();
  1185.             array_shift($parse);
  1186.         }
  1187.         $sql  =   $this->parseSql($sql,$parse);
  1188.         return $this->db->execute($sql);
  1189.     }

  1190.     /**
  1191.      * 解析SQL语句
  1192.      * @access public
  1193.      * @param string $sql  SQL指令
  1194.      * @param boolean $parse  是否需要解析SQL
  1195.      * @return string
  1196.      */
  1197.     protected function parseSql($sql,$parse) {
  1198.         // 分析表达式
  1199.         if(true === $parse) {
  1200.             $options =  $this->_parseOptions();
  1201.             $sql    =   $this->db->parseSql($sql,$options);
  1202.         }elseif(is_array($parse)){ // SQL预处理
  1203.             $parse  =   array_map(array($this->db,'escapeString'),$parse);
  1204.             $sql    =   vsprintf($sql,$parse);
  1205.         }else{
  1206.             $sql    =   strtr($sql,array('__TABLE__'=>$this->getTableName(),'__PREFIX__'=>$this->tablePrefix));
  1207.             $prefix =   $this->tablePrefix;
  1208.             $sql    =   preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $sql);
  1209.         }
  1210.         $this->db->setModel($this->name);
  1211.         return $sql;
  1212.     }

  1213.     /**
  1214.      * 切换当前的数据库连接
  1215.      * @access public
  1216.      * @param integer $linkNum  连接序号
  1217.      * @param mixed $config  数据库连接信息
  1218.      * @param boolean $force 强制重新连接
  1219.      * @return Model
  1220.      */
  1221.     public function db($linkNum='',$config='',$force=false) {
  1222.         if('' === $linkNum && $this->db) {
  1223.             return $this->db;
  1224.         }

  1225.         static $_db = array();
  1226.         if(!isset($_db[$linkNum]) || $force ) {
  1227.             // 创建一个新的实例
  1228.             if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持读取配置参数
  1229.                 $config  =  C($config);
  1230.             }
  1231.             $_db[$linkNum]            =    Db::getInstance($config);
  1232.         }elseif(NULL === $config){
  1233.             $_db[$linkNum]->close(); // 关闭数据库连接
  1234.             unset($_db[$linkNum]);
  1235.             return ;
  1236.         }

  1237.         // 切换数据库连接
  1238.         $this->db   =    $_db[$linkNum];
  1239.         $this->_after_db();
  1240.         // 字段检测
  1241.         if(!empty($this->name) && $this->autoCheckFields)    $this->_checkTableInfo();
  1242.         return $this;
  1243.     }
  1244.     // 数据库切换后回调方法
  1245.     protected function _after_db() {}

  1246.     /**
  1247.      * 得到当前的数据对象名称
  1248.      * @access public
  1249.      * @return string
  1250.      */
  1251.     public function getModelName() {
  1252.         if(empty($this->name)){
  1253.             $name = substr(get_class($this),0,-strlen(C('DEFAULT_M_LAYER')));
  1254.             if ( $pos = strrpos($name,'\\') ) {//有命名空间
  1255.                 $this->name = substr($name,$pos+1);
  1256.             }else{
  1257.                 $this->name = $name;
  1258.             }
  1259.         }
  1260.         return $this->name;
  1261.     }

  1262.     /**
  1263.      * 得到完整的数据表名
  1264.      * @access public
  1265.      * @return string
  1266.      */
  1267.     public function getTableName() {
  1268.         if(empty($this->trueTableName)) {
  1269.             $tableName  = !empty($this->tablePrefix) ? $this->tablePrefix : '';
  1270.             if(!empty($this->tableName)) {
  1271.                 $tableName .= $this->tableName;
  1272.             }else{
  1273.                 $tableName .= parse_name($this->name);
  1274.             }
  1275.             $this->trueTableName    =   strtolower($tableName);
  1276.         }
  1277.         return (!empty($this->dbName)?$this->dbName.'.':'').$this->trueTableName;
  1278.     }

  1279.     /**
  1280.      * 启动事务
  1281.      * @access public
  1282.      * @return void
  1283.      */
  1284.     public function startTrans() {
  1285.         $this->commit();
  1286.         $this->db->startTrans();
  1287.         return ;
  1288.     }

  1289.     /**
  1290.      * 提交事务
  1291.      * @access public
  1292.      * @return boolean
  1293.      */
  1294.     public function commit() {
  1295.         return $this->db->commit();
  1296.     }

  1297.     /**
  1298.      * 事务回滚
  1299.      * @access public
  1300.      * @return boolean
  1301.      */
  1302.     public function rollback() {
  1303.         return $this->db->rollback();
  1304.     }

  1305.     /**
  1306.      * 返回模型的错误信息
  1307.      * @access public
  1308.      * @return string
  1309.      */
  1310.     public function getError(){
  1311.         return $this->error;
  1312.     }

  1313.     /**
  1314.      * 返回数据库的错误信息
  1315.      * @access public
  1316.      * @return string
  1317.      */
  1318.     public function getDbError() {
  1319.         return $this->db->getError();
  1320.     }

  1321.     /**
  1322.      * 返回最后插入的ID
  1323.      * @access public
  1324.      * @return string
  1325.      */
  1326.     public function getLastInsID() {
  1327.         return $this->db->getLastInsID();
  1328.     }

  1329.     /**
  1330.      * 返回最后执行的sql语句
  1331.      * @access public
  1332.      * @return string
  1333.      */
  1334.     public function getLastSql() {
  1335.         return $this->db->getLastSql($this->name);
  1336.     }
  1337.     // 鉴于getLastSql比较常用 增加_sql 别名
  1338.     public function _sql(){
  1339.         return $this->getLastSql();
  1340.     }

  1341.     /**
  1342.      * 获取主键名称
  1343.      * @access public
  1344.      * @return string
  1345.      */
  1346.     public function getPk() {
  1347.         return $this->pk;
  1348.     }

  1349.     /**
  1350.      * 获取数据表字段信息
  1351.      * @access public
  1352.      * @return array
  1353.      */
  1354.     public function getDbFields(){
  1355.         if(isset($this->options['table'])) {// 动态指定表名
  1356.             $array      =   explode(' ',$this->options['table']);
  1357.             $fields     =   $this->db->getFields($array[0]);
  1358.             return  $fields?array_keys($fields):false;
  1359.         }
  1360.         if($this->fields) {
  1361.             $fields     =  $this->fields;
  1362.             unset($fields['_type'],$fields['_pk']);
  1363.             return $fields;
  1364.         }
  1365.         return false;
  1366.     }

  1367.     /**
  1368.      * 设置数据对象值
  1369.      * @access public
  1370.      * @param mixed $data 数据
  1371.      * @return Model
  1372.      */
  1373.     public function data($data=''){
  1374.         if('' === $data && !empty($this->data)) {
  1375.             return $this->data;
  1376.         }
  1377.         if(is_object($data)){
  1378.             $data   =   get_object_vars($data);
  1379.         }elseif(is_string($data)){
  1380.             parse_str($data,$data);
  1381.         }elseif(!is_array($data)){
  1382.             E(L('_DATA_TYPE_INVALID_'));
  1383.         }
  1384.         $this->data = $data;
  1385.         return $this;
  1386.     }

  1387.     /**
  1388.      * 指定当前的数据表
  1389.      * @access public
  1390.      * @param mixed $table
  1391.      * @return Model
  1392.      */
  1393.     public function table($table) {
  1394.         $prefix =   $this->tablePrefix;
  1395.         if(is_array($table)) {
  1396.             $this->options['table'] =   $table;
  1397.         }elseif(!empty($table)) {
  1398.             //将__TABLE_NAME__替换成带前缀的表名
  1399.             $table  = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $table);
  1400.             $this->options['table'] =   $table;
  1401.         }
  1402.         return $this;
  1403.     }

  1404.     /**
  1405.      * 查询SQL组装 join
  1406.      * @access public
  1407.      * @param mixed $join
  1408.      * @param string $type JOIN类型
  1409.      * @return Model
  1410.      */
  1411.     public function join($join,$type='INNER') {
  1412.         $prefix =   $this->tablePrefix;
  1413.         if(is_array($join)) {
  1414.             foreach ($join as $key=>&$_join){
  1415.                 $_join  =   preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $_join);
  1416.                 $_join  =   false !== stripos($_join,'JOIN')? $_join : $type.' JOIN ' .$_join;
  1417.             }
  1418.             $this->options['join']      =   $join;
  1419.         }elseif(!empty($join)) {
  1420.             //将__TABLE_NAME__字符串替换成带前缀的表名
  1421.             $join  = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $join);
  1422.             $this->options['join'][]    =   false !== stripos($join,'JOIN')? $join : $type.' JOIN '.$join;
  1423.         }
  1424.         return $this;
  1425.     }

  1426.     /**
  1427.      * 查询SQL组装 union
  1428.      * @access public
  1429.      * @param mixed $union
  1430.      * @param boolean $all
  1431.      * @return Model
  1432.      */
  1433.     public function union($union,$all=false) {
  1434.         if(empty($union)) return $this;
  1435.         if($all) {
  1436.             $this->options['union']['_all']  =   true;
  1437.         }
  1438.         if(is_object($union)) {
  1439.             $union   =  get_object_vars($union);
  1440.         }
  1441.         // 转换union表达式
  1442.         if(is_string($union) ) {
  1443.             $prefix =   $this->tablePrefix;
  1444.             //将__TABLE_NAME__字符串替换成带前缀的表名
  1445.             $options  = preg_replace_callback("/__([A-Z_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $union);
  1446.         }elseif(is_array($union)){
  1447.             if(isset($union[0])) {
  1448.                 $this->options['union']  =  array_merge($this->options['union'],$union);
  1449.                 return $this;
  1450.             }else{
  1451.                 $options =  $union;
  1452.             }
  1453.         }else{
  1454.             E(L('_DATA_TYPE_INVALID_'));
  1455.         }
  1456.         $this->options['union'][]  =   $options;
  1457.         return $this;
  1458.     }

  1459.     /**
  1460.      * 查询缓存
  1461.      * @access public
  1462.      * @param mixed $key
  1463.      * @param integer $expire
  1464.      * @param string $type
  1465.      * @return Model
  1466.      */
  1467.     public function cache($key=true,$expire=null,$type=''){
  1468.         if(false !== $key)
  1469.             $this->options['cache']  =  array('key'=>$key,'expire'=>$expire,'type'=>$type);
  1470.         return $this;
  1471.     }

  1472.     /**
  1473.      * 指定查询字段 支持字段排除
  1474.      * @access public
  1475.      * @param mixed $field
  1476.      * @param boolean $except 是否排除
  1477.      * @return Model
  1478.      */
  1479.     public function field($field,$except=false){
  1480.         if(true === $field) {// 获取全部字段
  1481.             $fields     =  $this->getDbFields();
  1482.             $field      =  $fields?:'*';
  1483.         }elseif($except) {// 字段排除
  1484.             if(is_string($field)) {
  1485.                 $field  =  explode(',',$field);
  1486.             }
  1487.             $fields     =  $this->getDbFields();
  1488.             $field      =  $fields?array_diff($fields,$field):$field;
  1489.         }
  1490.         $this->options['field']   =   $field;
  1491.         return $this;
  1492.     }

  1493.     /**
  1494.      * 调用命名范围
  1495.      * @access public
  1496.      * @param mixed $scope 命名范围名称 支持多个 和直接定义
  1497.      * @param array $args 参数
  1498.      * @return Model
  1499.      */
  1500.     public function scope($scope='',$args=NULL){
  1501.         if('' === $scope) {
  1502.             if(isset($this->_scope['default'])) {
  1503.                 // 默认的命名范围
  1504.                 $options    =   $this->_scope['default'];
  1505.             }else{
  1506.                 return $this;
  1507.             }
  1508.         }elseif(is_string($scope)){ // 支持多个命名范围调用 用逗号分割
  1509.             $scopes         =   explode(',',$scope);
  1510.             $options        =   array();
  1511.             foreach ($scopes as $name){
  1512.                 if(!isset($this->_scope[$name])) continue;
  1513.                 $options    =   array_merge($options,$this->_scope[$name]);
  1514.             }
  1515.             if(!empty($args) && is_array($args)) {
  1516.                 $options    =   array_merge($options,$args);
  1517.             }
  1518.         }elseif(is_array($scope)){ // 直接传入命名范围定义
  1519.             $options        =   $scope;
  1520.         }
  1521.         
  1522.         if(is_array($options) && !empty($options)){
  1523.             $this->options  =   array_merge($this->options,array_change_key_case($options));
  1524.         }
  1525.         return $this;
  1526.     }

  1527.     /**
  1528.      * 指定查询条件 支持安全过滤
  1529.      * @access public
  1530.      * @param mixed $where 条件表达式
  1531.      * @param mixed $parse 预处理参数
  1532.      * @return Model
  1533.      */
  1534.     public function where($where,$parse=null){
  1535.         if(!is_null($parse) && is_string($where)) {
  1536.             if(!is_array($parse)) {
  1537.                 $parse = func_get_args();
  1538.                 array_shift($parse);
  1539.             }
  1540.             $parse = array_map(array($this->db,'escapeString'),$parse);
  1541.             $where =   vsprintf($where,$parse);
  1542.         }elseif(is_object($where)){
  1543.             $where  =   get_object_vars($where);
  1544.         }
  1545.         if(is_string($where) && '' != $where){
  1546.             $map    =   array();
  1547.             $map['_string']   =   $where;
  1548.             $where  =   $map;
  1549.         }        
  1550.         if(isset($this->options['where'])){
  1551.             $this->options['where'] =   array_merge($this->options['where'],$where);
  1552.         }else{
  1553.             $this->options['where'] =   $where;
  1554.         }
  1555.         
  1556.         return $this;
  1557.     }

  1558.     /**
  1559.      * 指定查询数量
  1560.      * @access public
  1561.      * @param mixed $offset 起始位置
  1562.      * @param mixed $length 查询数量
  1563.      * @return Model
  1564.      */
  1565.     public function limit($offset,$length=null){
  1566.         if(is_null($length) && strpos($offset,',')){
  1567.             list($offset,$length)   =   explode(',',$offset);
  1568.         }
  1569.         $this->options['limit']     =   intval($offset).( $length? ','.intval($length) : '' );
  1570.         return $this;
  1571.     }

  1572.     /**
  1573.      * 指定分页
  1574.      * @access public
  1575.      * @param mixed $page 页数
  1576.      * @param mixed $listRows 每页数量
  1577.      * @return Model
  1578.      */
  1579.     public function page($page,$listRows=null){
  1580.         if(is_null($listRows) && strpos($page,',')){
  1581.             list($page,$listRows)   =   explode(',',$page);
  1582.         }
  1583.         $this->options['page']      =   array(intval($page),intval($listRows));
  1584.         return $this;
  1585.     }

  1586.     /**
  1587.      * 查询注释
  1588.      * @access public
  1589.      * @param string $comment 注释
  1590.      * @return Model
  1591.      */
  1592.     public function comment($comment){
  1593.         $this->options['comment'] =   $comment;
  1594.         return $this;
  1595.     }

  1596.     /**
  1597.      * 参数绑定
  1598.      * @access public
  1599.      * @param string $key  参数名
  1600.      * @param mixed $value  绑定的变量及绑定参数
  1601.      * @return Model
  1602.      */
  1603.     public function bind($key,$value=false) {
  1604.         if(is_array($key)){
  1605.             $this->options['bind'] =    $key;
  1606.         }else{
  1607.             $num =  func_num_args();
  1608.             if($num>2){
  1609.                 $params =   func_get_args();
  1610.                 array_shift($params);
  1611.                 $this->options['bind'][$key] =  $params;
  1612.             }else{
  1613.                 $this->options['bind'][$key] =  $value;
  1614.             }        
  1615.         }
  1616.         return $this;
  1617.     }

  1618.     /**
  1619.      * 设置模型的属性值
  1620.      * @access public
  1621.      * @param string $name 名称
  1622.      * @param mixed $value 值
  1623.      * @return Model
  1624.      */
  1625.     public function setProperty($name,$value) {
  1626.         if(property_exists($this,$name))
  1627.             $this->$name = $value;
  1628.         return $this;
  1629.     }

  1630. }
复制代码


回复

使用道具 举报

您需要登录后才可以回帖 登录 | ע

本版积分规则

QQ|Archiver|ֻ|С|ѡ̳

GMT+8, 2026-5-2 04:39 , Processed in 0.079610 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表
0.0935s