123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- <?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 <liu21st@gmail.com>
- // +----------------------------------------------------------------------
- namespace Think\Model;
- use Think\Model;
- /**
- * 高级模型扩展
- */
- class AdvModel extends Model {
- protected $optimLock = 'lock_version';
- protected $returnType = 'array';
- protected $blobFields = array();
- protected $blobValues = null;
- protected $serializeField = array();
- protected $readonlyField = array();
- protected $_filter = array();
- protected $partition = array();
- public function __construct($name='',$tablePrefix='',$connection='') {
- if('' !== $name || is_subclass_of($this,'AdvModel') ){
- // 如果是AdvModel子类或者有传入模型名称则获取字段缓存
- }else{
- // 空的模型 关闭字段缓存
- $this->autoCheckFields = false;
- }
- parent::__construct($name,$tablePrefix,$connection);
- }
- /**
- * 利用__call方法重载 实现一些特殊的Model方法 (魔术方法)
- * @access public
- * @param string $method 方法名称
- * @param mixed $args 调用参数
- * @return mixed
- */
- public function __call($method,$args) {
- if(strtolower(substr($method,0,3))=='top'){
- // 获取前N条记录
- $count = substr($method,3);
- array_unshift($args,$count);
- return call_user_func_array(array(&$this, 'topN'), $args);
- }else{
- return parent::__call($method,$args);
- }
- }
- /**
- * 对保存到数据库的数据进行处理
- * @access protected
- * @param mixed $data 要操作的数据
- * @return boolean
- */
- protected function _facade($data) {
- // 检查序列化字段
- $data = $this->serializeField($data);
- return parent::_facade($data);
- }
- // 查询成功后的回调方法
- protected function _after_find(&$result,$options='') {
- // 检查序列化字段
- $this->checkSerializeField($result);
- // 获取文本字段
- $this->getBlobFields($result);
- // 检查字段过滤
- $result = $this->getFilterFields($result);
- // 缓存乐观锁
- $this->cacheLockVersion($result);
- }
- // 查询数据集成功后的回调方法
- protected function _after_select(&$resultSet,$options='') {
- // 检查序列化字段
- $resultSet = $this->checkListSerializeField($resultSet);
- // 获取文本字段
- $resultSet = $this->getListBlobFields($resultSet);
- // 检查列表字段过滤
- $resultSet = $this->getFilterListFields($resultSet);
- }
- // 写入前的回调方法
- protected function _before_insert(&$data,$options='') {
- // 记录乐观锁
- $data = $this->recordLockVersion($data);
- // 检查文本字段
- $data = $this->checkBlobFields($data);
- // 检查字段过滤
- $data = $this->setFilterFields($data);
- }
- protected function _after_insert($data,$options) {
- // 保存文本字段
- $this->saveBlobFields($data);
- }
- // 更新前的回调方法
- protected function _before_update(&$data,$options='') {
- // 检查乐观锁
- $pk = $this->getPK();
- if(isset($options['where'][$pk])){
- $id = $options['where'][$pk];
- if(!$this->checkLockVersion($id,$data)) {
- return false;
- }
- }
- // 检查文本字段
- $data = $this->checkBlobFields($data);
- // 检查只读字段
- $data = $this->checkReadonlyField($data);
- // 检查字段过滤
- $data = $this->setFilterFields($data);
- }
- protected function _after_update($data,$options) {
- // 保存文本字段
- $this->saveBlobFields($data);
- }
- protected function _after_delete($data,$options) {
- // 删除Blob数据
- $this->delBlobFields($data);
- }
- /**
- * 记录乐观锁
- * @access protected
- * @param array $data 数据对象
- * @return array
- */
- protected function recordLockVersion($data) {
- // 记录乐观锁
- if($this->optimLock && !isset($data[$this->optimLock]) ) {
- if(in_array($this->optimLock,$this->fields,true)) {
- $data[$this->optimLock] = 0;
- }
- }
- return $data;
- }
- /**
- * 缓存乐观锁
- * @access protected
- * @param array $data 数据对象
- * @return void
- */
- protected function cacheLockVersion($data) {
- if($this->optimLock) {
- if(isset($data[$this->optimLock]) && isset($data[$this->getPk()])) {
- // 只有当存在乐观锁字段和主键有值的时候才记录乐观锁
- $_SESSION[$this->name.'_'.$data[$this->getPk()].'_lock_version'] = $data[$this->optimLock];
- }
- }
- }
- /**
- * 检查乐观锁
- * @access protected
- * @param inteter $id 当前主键
- * @param array $data 当前数据
- * @return mixed
- */
- protected function checkLockVersion($id,&$data) {
- // 检查乐观锁
- $identify = $this->name.'_'.$id.'_lock_version';
- if($this->optimLock && isset($_SESSION[$identify])) {
- $lock_version = $_SESSION[$identify];
- $vo = $this->field($this->optimLock)->find($id);
- $_SESSION[$identify] = $lock_version;
- $curr_version = $vo[$this->optimLock];
- if(isset($curr_version)) {
- if($curr_version>0 && $lock_version != $curr_version) {
- // 记录已经更新
- $this->error = L('_RECORD_HAS_UPDATE_');
- return false;
- }else{
- // 更新乐观锁
- $save_version = $data[$this->optimLock];
- if($save_version != $lock_version+1) {
- $data[$this->optimLock] = $lock_version+1;
- }
- $_SESSION[$identify] = $lock_version+1;
- }
- }
- }
- return true;
- }
- /**
- * 查找前N个记录
- * @access public
- * @param integer $count 记录个数
- * @param array $options 查询表达式
- * @return array
- */
- public function topN($count,$options=array()) {
- $options['limit'] = $count;
- return $this->select($options);
- }
- /**
- * 查询符合条件的第N条记录
- * 0 表示第一条记录 -1 表示最后一条记录
- * @access public
- * @param integer $position 记录位置
- * @param array $options 查询表达式
- * @return mixed
- */
- public function getN($position=0,$options=array()) {
- if($position>=0) { // 正向查找
- $options['limit'] = $position.',1';
- $list = $this->select($options);
- return $list?$list[0]:false;
- }else{ // 逆序查找
- $list = $this->select($options);
- return $list?$list[count($list)-abs($position)]:false;
- }
- }
- /**
- * 获取满足条件的第一条记录
- * @access public
- * @param array $options 查询表达式
- * @return mixed
- */
- public function first($options=array()) {
- return $this->getN(0,$options);
- }
- /**
- * 获取满足条件的最后一条记录
- * @access public
- * @param array $options 查询表达式
- * @return mixed
- */
- public function last($options=array()) {
- return $this->getN(-1,$options);
- }
- /**
- * 返回数据
- * @access public
- * @param array $data 数据
- * @param string $type 返回类型 默认为数组
- * @return mixed
- */
- public function returnResult($data,$type='') {
- if('' === $type)
- $type = $this->returnType;
- switch($type) {
- case 'array' : return $data;
- case 'object': return (object)$data;
- default:// 允许用户自定义返回类型
- if(class_exists($type))
- return new $type($data);
- else
- E(L('_CLASS_NOT_EXIST_').':'.$type);
- }
- }
- /**
- * 获取数据的时候过滤数据字段
- * @access protected
- * @param mixed $result 查询的数据
- * @return array
- */
- protected function getFilterFields(&$result) {
- if(!empty($this->_filter)) {
- foreach ($this->_filter as $field=>$filter){
- if(isset($result[$field])) {
- $fun = $filter[1];
- if(!empty($fun)) {
- if(isset($filter[2]) && $filter[2]){
- // 传递整个数据对象作为参数
- $result[$field] = call_user_func($fun,$result);
- }else{
- // 传递字段的值作为参数
- $result[$field] = call_user_func($fun,$result[$field]);
- }
- }
- }
- }
- }
- return $result;
- }
- protected function getFilterListFields(&$resultSet) {
- if(!empty($this->_filter)) {
- foreach ($resultSet as $key=>$result)
- $resultSet[$key] = $this->getFilterFields($result);
- }
- return $resultSet;
- }
- /**
- * 写入数据的时候过滤数据字段
- * @access protected
- * @param mixed $result 查询的数据
- * @return array
- */
- protected function setFilterFields($data) {
- if(!empty($this->_filter)) {
- foreach ($this->_filter as $field=>$filter){
- if(isset($data[$field])) {
- $fun = $filter[0];
- if(!empty($fun)) {
- if(isset($filter[2]) && $filter[2]) {
- // 传递整个数据对象作为参数
- $data[$field] = call_user_func($fun,$data);
- }else{
- // 传递字段的值作为参数
- $data[$field] = call_user_func($fun,$data[$field]);
- }
- }
- }
- }
- }
- return $data;
- }
- /**
- * 返回数据列表
- * @access protected
- * @param array $resultSet 数据
- * @param string $type 返回类型 默认为数组
- * @return void
- */
- protected function returnResultSet(&$resultSet,$type='') {
- foreach ($resultSet as $key=>$data)
- $resultSet[$key] = $this->returnResult($data,$type);
- return $resultSet;
- }
- protected function checkBlobFields(&$data) {
- // 检查Blob文件保存字段
- if(!empty($this->blobFields)) {
- foreach ($this->blobFields as $field){
- if(isset($data[$field])) {
- if(isset($data[$this->getPk()]))
- $this->blobValues[$this->name.'/'.$data[$this->getPk()].'_'.$field] = $data[$field];
- else
- $this->blobValues[$this->name.'/@?id@_'.$field] = $data[$field];
- unset($data[$field]);
- }
- }
- }
- return $data;
- }
- /**
- * 获取数据集的文本字段
- * @access protected
- * @param mixed $resultSet 查询的数据
- * @param string $field 查询的字段
- * @return void
- */
- protected function getListBlobFields(&$resultSet,$field='') {
- if(!empty($this->blobFields)) {
- foreach ($resultSet as $key=>$result){
- $result = $this->getBlobFields($result,$field);
- $resultSet[$key] = $result;
- }
- }
- return $resultSet;
- }
- /**
- * 获取数据的文本字段
- * @access protected
- * @param mixed $data 查询的数据
- * @param string $field 查询的字段
- * @return void
- */
- protected function getBlobFields(&$data,$field='') {
- if(!empty($this->blobFields)) {
- $pk = $this->getPk();
- $id = $data[$pk];
- if(empty($field)) {
- foreach ($this->blobFields as $field){
- $identify = $this->name.'/'.$id.'_'.$field;
- $data[$field] = F($identify);
- }
- return $data;
- }else{
- $identify = $this->name.'/'.$id.'_'.$field;
- return F($identify);
- }
- }
- }
- /**
- * 保存File方式的字段
- * @access protected
- * @param mixed $data 保存的数据
- * @return void
- */
- protected function saveBlobFields(&$data) {
- if(!empty($this->blobFields)) {
- foreach ($this->blobValues as $key=>$val){
- if(strpos($key,'@?id@'))
- $key = str_replace('@?id@',$data[$this->getPk()],$key);
- F($key,$val);
- }
- }
- }
- /**
- * 删除File方式的字段
- * @access protected
- * @param mixed $data 保存的数据
- * @param string $field 查询的字段
- * @return void
- */
- protected function delBlobFields(&$data,$field='') {
- if(!empty($this->blobFields)) {
- $pk = $this->getPk();
- $id = $data[$pk];
- if(empty($field)) {
- foreach ($this->blobFields as $field){
- $identify = $this->name.'/'.$id.'_'.$field;
- F($identify,null);
- }
- }else{
- $identify = $this->name.'/'.$id.'_'.$field;
- F($identify,null);
- }
- }
- }
- /**
- * 检查序列化数据字段
- * @access protected
- * @param array $data 数据
- * @return array
- */
- protected function serializeField(&$data) {
- // 检查序列化字段
- if(!empty($this->serializeField)) {
- // 定义方式 $this->serializeField = array('ser'=>array('name','email'));
- foreach ($this->serializeField as $key=>$val){
- if(empty($data[$key])) {
- $serialize = array();
- foreach ($val as $name){
- if(isset($data[$name])) {
- $serialize[$name] = $data[$name];
- unset($data[$name]);
- }
- }
- if(!empty($serialize)) {
- $data[$key] = serialize($serialize);
- }
- }
- }
- }
- return $data;
- }
- // 检查返回数据的序列化字段
- protected function checkSerializeField(&$result) {
- // 检查序列化字段
- if(!empty($this->serializeField)) {
- foreach ($this->serializeField as $key=>$val){
- if(isset($result[$key])) {
- $serialize = unserialize($result[$key]);
- foreach ($serialize as $name=>$value)
- $result[$name] = $value;
- unset($serialize,$result[$key]);
- }
- }
- }
- return $result;
- }
- // 检查数据集的序列化字段
- protected function checkListSerializeField(&$resultSet) {
- // 检查序列化字段
- if(!empty($this->serializeField)) {
- foreach ($this->serializeField as $key=>$val){
- foreach ($resultSet as $k=>$result){
- if(isset($result[$key])) {
- $serialize = unserialize($result[$key]);
- foreach ($serialize as $name=>$value)
- $result[$name] = $value;
- unset($serialize,$result[$key]);
- $resultSet[$k] = $result;
- }
- }
- }
- }
- return $resultSet;
- }
- /**
- * 检查只读字段
- * @access protected
- * @param array $data 数据
- * @return array
- */
- protected function checkReadonlyField(&$data) {
- if(!empty($this->readonlyField)) {
- foreach ($this->readonlyField as $key=>$field){
- if(isset($data[$field]))
- unset($data[$field]);
- }
- }
- return $data;
- }
- /**
- * 批处理执行SQL语句
- * 批处理的指令都认为是execute操作
- * @access public
- * @param array $sql SQL批处理指令
- * @return boolean
- */
- public function patchQuery($sql=array()) {
- if(!is_array($sql)) return false;
- // 自动启动事务支持
- $this->startTrans();
- try{
- foreach ($sql as $_sql){
- $result = $this->execute($_sql);
- if(false === $result) {
- // 发生错误自动回滚事务
- $this->rollback();
- return false;
- }
- }
- // 提交事务
- $this->commit();
- } catch (ThinkException $e) {
- $this->rollback();
- }
- return true;
- }
- /**
- * 得到分表的的数据表名
- * @access public
- * @param array $data 操作的数据
- * @return string
- */
- public function getPartitionTableName($data=array()) {
- // 对数据表进行分区
- if(isset($data[$this->partition['field']])) {
- $field = $data[$this->partition['field']];
- switch($this->partition['type']) {
- case 'id':
- // 按照id范围分表
- $step = $this->partition['expr'];
- $seq = floor($field / $step)+1;
- break;
- case 'year':
- // 按照年份分表
- if(!is_numeric($field)) {
- $field = strtotime($field);
- }
- $seq = date('Y',$field)-$this->partition['expr']+1;
- break;
- case 'mod':
- // 按照id的模数分表
- $seq = ($field % $this->partition['num'])+1;
- break;
- case 'md5':
- // 按照md5的序列分表
- $seq = (ord(substr(md5($field),0,1)) % $this->partition['num'])+1;
- break;
- default :
- if(function_exists($this->partition['type'])) {
- // 支持指定函数哈希
- $fun = $this->partition['type'];
- $seq = (ord(substr($fun($field),0,1)) % $this->partition['num'])+1;
- }else{
- // 按照字段的首字母的值分表
- $seq = (ord($field{0}) % $this->partition['num'])+1;
- }
- }
- return $this->getTableName().'_'.$seq;
- }else{
- // 当设置的分表字段不在查询条件或者数据中
- // 进行联合查询,必须设定 partition['num']
- $tableName = array();
- for($i=0;$i<$this->partition['num'];$i++)
- $tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1);
- $tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name;
- return $tableName;
- }
- }
- }
|