【thinkphp3.x】Model.class.php文件分析
2012-10-04 02:49
549 查看
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
/**
* ThinkPHP Model模型类
* 实现了ORM和ActiveRecords模式
* @category Think
* @package Think
* @subpackage Core
* @author liu21st <liu21st@gmail.com>
*/
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;// 表单值不为空则验证
// 当前使用的扩展模型
private $_extModel = null;
// 当前数据库操作对象
protected $db = null;
// 主键名称
protected $pk = 'id';
// 数据表前缀
protected $tablePrefix = '';
// 模型名称
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('table','order','alias','having','group','lock','distinct','auto','filter','validate');
/**
* 架构函数
* 取得DB类的实例对象 字段检查
* @access public
* @param string $name 模型名称
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
*/
public function __construct($name='',$tablePrefix='',$connection='') {
// 模型初始化
$this->_initialize();
// 获取模型名称
if(!empty($name)) {
if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
//属性1-数据库名 属性2-模型表名(数据表名)
list($this->dbName,$this->name) = explode('.',$name);
}else{//模型名
$this->name = $name;
}
}elseif(empty($this->name)){
//模型名为空的情况
$this->name = $this->getModelName();
}
// 设置表前缀,检查$tablePrefix表前缀参数是否为Null,注意:不是检查表前缀参数是否为空
if(is_null($tablePrefix)) {// 如果表前缀参数为Null表示没有前缀
$this->tablePrefix = '';//没有前缀的情况
}elseif('' != $tablePrefix) {//如果表前缀参数不为空
$this->tablePrefix = $tablePrefix;
}else{
//默认执行:如果类中未指定表前缀,那么将采用系统默认指定的表前缀
$this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX');
}
// 数据库初始化操作
// 获取数据库操作对象
// 当前模型有独立的数据库连接信息
$this->db(0,empty($this->connection)?$connection:$this->connection);
}
/**
* 自动检测数据表信息
* @access protected
* @return void
*/
protected function _checkTableInfo() {
// 如果不是Model类 自动记录数据表信息
// 只在第一次执行记录
if(empty($this->fields)) {//默认执行
// 如果数据表字段没有定义则自动获取
//调试模式下不执行
if(C('DB_FIELDS_CACHE')) {//字段缓存的启用
$db = $this->dbName?$this->dbName:C('DB_NAME');//数据库名
$fields = F('_fields/'.strtolower($db.'.'.$this->name));
if($fields) {
$version = C('DB_FIELD_VERISON');
if(empty($version) || $fields['_version']== $version) {
$this->fields = $fields;
return ;
}
}
}
// 每次都会读取数据表信息
$this->flush();
}
}
/**
* 获取字段信息并缓存
* @access public
* @return void
*/
public function flush() {
//$this->db:数据库驱动类Db,class.php实例化对象,查看$this->db()方法
// 缓存不存在则查询数据表信息
//var_dump($this->db);//DbMysql.class.php驱动类对象
//setModel()方法为Db.class.php中方法
//class DbMysql extends Db{}
$this->db->setModel($this->name);
//$this->getTableName():获取表名,如:think_user
//getFields方法获取数据表每个字段的信息
/**
*[id] => Array(
* [name] => id
* [type] => smallint(4) unsigned
* [notnull] =>
* [default] =>
* [primary] => 1
* [autoinc] => 1
* )
*/
$fields = $this->db->getFields($this->getTableName());
if(!$fields) { // 无法获取字段信息
return false;
}
/**
* Array(
* [0] => id
* [1] => title
* [2] => content
* [3] => create_time
* )
*/
$this->fields = array_keys($fields);
$this->fields['_autoinc'] = false;
//注意:循环的不是$this->fields
foreach ($fields as $key=>$val){
// 记录字段类型
$type[$key] = $val['type'];//如:$type[id]=int
if($val['primary']) {
$this->fields['_pk'] = $key;//主键字段,这里的$key为字段名
if($val['autoinc']) $this->fields['_autoinc'] = true;//自动增长
}
}
// 记录字段类型信息
$this->fields['_type'] = $type;//表中各个字段的类型
if(C('DB_FIELD_VERISON')) $this->fields['_version'] = C('DB_FIELD_VERISON');
// 2008-3-7 增加缓存开关控制
//调试模式下,为禁用,即:不会缓存
if(C('DB_FIELDS_CACHE')){
// 永久缓存数据表信息
$db = $this->dbName?$this->dbName:C('DB_NAME');
F('_fields/'.strtolower($db.'.'.$this->name),$this->fields);
}
}
/**
* 动态切换扩展模型
* @access public
* @param string $type 模型类型名称
* @param mixed $vars 要传入扩展模型的属性变量
* @return Model
*/
public function switchModel($type,$vars=array()) {
$class = ucwords(strtolower($type)).'Model';
if(!class_exists($class))
throw_exception($class.L('_MODEL_NOT_EXIST_'));
// 实例化扩展模型
$this->_extModel = new $class($this->name);
if(!empty($vars)) {
// 传入当前模型的属性到扩展模型
foreach ($vars as $var)
$this->_extModel->setProperty($var,$this->$var);
}
return $this->_extModel;
}
/**
* 设置数据对象的值
* @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{
throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
return;
}
}
// 回调方法 初始化模型
protected function _initialize() {}
/**
* 对保存到数据库的数据进行处理
* @access protected
* @param mixed $data 要操作的数据
* @return boolean
*/
protected function _facade($data) {
// 检查非数据字段
if(!empty($this->fields)) {
foreach ($data as $key=>$val){
if(!in_array($key,$this->fields,true)){
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) {}
/**
* 新增数据
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @param boolean $replace 是否replace
* @return mixed
*/
public function add($data='',$options=array(),$replace=false) {
if(empty($data)) {
// 没有传递数据,获取当前数据对象的值
if(!empty($this->data)) {
$data = $this->data;//数据对象中的数据
// 重置数据
$this->data = array();
}else{
$this->error = L('_DATA_TYPE_INVALID_');
return false;
}
}
// 分析表达式
$options = $this->_parseOptions($options);
// 数据处理
$data = $this->_facade($data);//对保存到数据库中的数据进行处理
if(false === $this->_before_insert($data,$options)) {
return false;
}
// 写入数据到数据库
/**
* $options:默认值有下面两个
* Array
(
=> think_form
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2012 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
/**
* ThinkPHP Model模型类
* 实现了ORM和ActiveRecords模式
* @category Think
* @package Think
* @subpackage Core
* @author liu21st <liu21st@gmail.com>
*/
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;// 表单值不为空则验证
// 当前使用的扩展模型
private $_extModel = null;
// 当前数据库操作对象
protected $db = null;
// 主键名称
protected $pk = 'id';
// 数据表前缀
protected $tablePrefix = '';
// 模型名称
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('table','order','alias','having','group','lock','distinct','auto','filter','validate');
/**
* 架构函数
* 取得DB类的实例对象 字段检查
* @access public
* @param string $name 模型名称
* @param string $tablePrefix 表前缀
* @param mixed $connection 数据库连接信息
*/
public function __construct($name='',$tablePrefix='',$connection='') {
// 模型初始化
$this->_initialize();
// 获取模型名称
if(!empty($name)) {
if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义
//属性1-数据库名 属性2-模型表名(数据表名)
list($this->dbName,$this->name) = explode('.',$name);
}else{//模型名
$this->name = $name;
}
}elseif(empty($this->name)){
//模型名为空的情况
$this->name = $this->getModelName();
}
// 设置表前缀,检查$tablePrefix表前缀参数是否为Null,注意:不是检查表前缀参数是否为空
if(is_null($tablePrefix)) {// 如果表前缀参数为Null表示没有前缀
$this->tablePrefix = '';//没有前缀的情况
}elseif('' != $tablePrefix) {//如果表前缀参数不为空
$this->tablePrefix = $tablePrefix;
}else{
//默认执行:如果类中未指定表前缀,那么将采用系统默认指定的表前缀
$this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX');
}
// 数据库初始化操作
// 获取数据库操作对象
// 当前模型有独立的数据库连接信息
$this->db(0,empty($this->connection)?$connection:$this->connection);
}
/**
* 自动检测数据表信息
* @access protected
* @return void
*/
protected function _checkTableInfo() {
// 如果不是Model类 自动记录数据表信息
// 只在第一次执行记录
if(empty($this->fields)) {//默认执行
// 如果数据表字段没有定义则自动获取
//调试模式下不执行
if(C('DB_FIELDS_CACHE')) {//字段缓存的启用
$db = $this->dbName?$this->dbName:C('DB_NAME');//数据库名
$fields = F('_fields/'.strtolower($db.'.'.$this->name));
if($fields) {
$version = C('DB_FIELD_VERISON');
if(empty($version) || $fields['_version']== $version) {
$this->fields = $fields;
return ;
}
}
}
// 每次都会读取数据表信息
$this->flush();
}
}
/**
* 获取字段信息并缓存
* @access public
* @return void
*/
public function flush() {
//$this->db:数据库驱动类Db,class.php实例化对象,查看$this->db()方法
// 缓存不存在则查询数据表信息
//var_dump($this->db);//DbMysql.class.php驱动类对象
//setModel()方法为Db.class.php中方法
//class DbMysql extends Db{}
$this->db->setModel($this->name);
//$this->getTableName():获取表名,如:think_user
//getFields方法获取数据表每个字段的信息
/**
*[id] => Array(
* [name] => id
* [type] => smallint(4) unsigned
* [notnull] =>
* [default] =>
* [primary] => 1
* [autoinc] => 1
* )
*/
$fields = $this->db->getFields($this->getTableName());
if(!$fields) { // 无法获取字段信息
return false;
}
/**
* Array(
* [0] => id
* [1] => title
* [2] => content
* [3] => create_time
* )
*/
$this->fields = array_keys($fields);
$this->fields['_autoinc'] = false;
//注意:循环的不是$this->fields
foreach ($fields as $key=>$val){
// 记录字段类型
$type[$key] = $val['type'];//如:$type[id]=int
if($val['primary']) {
$this->fields['_pk'] = $key;//主键字段,这里的$key为字段名
if($val['autoinc']) $this->fields['_autoinc'] = true;//自动增长
}
}
// 记录字段类型信息
$this->fields['_type'] = $type;//表中各个字段的类型
if(C('DB_FIELD_VERISON')) $this->fields['_version'] = C('DB_FIELD_VERISON');
// 2008-3-7 增加缓存开关控制
//调试模式下,为禁用,即:不会缓存
if(C('DB_FIELDS_CACHE')){
// 永久缓存数据表信息
$db = $this->dbName?$this->dbName:C('DB_NAME');
F('_fields/'.strtolower($db.'.'.$this->name),$this->fields);
}
}
/**
* 动态切换扩展模型
* @access public
* @param string $type 模型类型名称
* @param mixed $vars 要传入扩展模型的属性变量
* @return Model
*/
public function switchModel($type,$vars=array()) {
$class = ucwords(strtolower($type)).'Model';
if(!class_exists($class))
throw_exception($class.L('_MODEL_NOT_EXIST_'));
// 实例化扩展模型
$this->_extModel = new $class($this->name);
if(!empty($vars)) {
// 传入当前模型的属性到扩展模型
foreach ($vars as $var)
$this->_extModel->setProperty($var,$this->$var);
}
return $this->_extModel;
}
/**
* 设置数据对象的值
* @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{
throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
return;
}
}
// 回调方法 初始化模型
protected function _initialize() {}
/**
* 对保存到数据库的数据进行处理
* @access protected
* @param mixed $data 要操作的数据
* @return boolean
*/
protected function _facade($data) {
// 检查非数据字段
if(!empty($this->fields)) {
foreach ($data as $key=>$val){
if(!in_array($key,$this->fields,true)){
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) {}
/**
* 新增数据
* @access public
* @param mixed $data 数据
* @param array $options 表达式
* @param boolean $replace 是否replace
* @return mixed
*/
public function add($data='',$options=array(),$replace=false) {
if(empty($data)) {
// 没有传递数据,获取当前数据对象的值
if(!empty($this->data)) {
$data = $this->data;//数据对象中的数据
// 重置数据
$this->data = array();
}else{
$this->error = L('_DATA_TYPE_INVALID_');
return false;
}
}
// 分析表达式
$options = $this->_parseOptions($options);
// 数据处理
$data = $this->_facade($data);//对保存到数据库中的数据进行处理
if(false === $this->_before_insert($data,$options)) {
return false;
}
// 写入数据到数据库
/**
* $options:默认值有下面两个
* Array
(