Traits/Think/Model/Extend 改进 增加统计查询和动态查询

This commit is contained in:
ThinkPHP
2013-04-22 14:40:35 +08:00
parent af39b51bcc
commit db91f81567

View File

@@ -1,231 +1,261 @@
<?php <?php
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | TOPThink [ WE CAN DO IT JUST THINK ] // | TOPThink [ WE CAN DO IT JUST THINK ]
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Copyright (c) 2011 http://topthink.com All rights reserved. // | Copyright (c) 2011 http://topthink.com All rights reserved.
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 ) // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com> // | Author: liu21st <liu21st@gmail.com>
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
namespace Traits\Think\Model; namespace Traits\Think\Model;
trait Extend { trait Extend {
protected $partition = []; protected $partition = [];
/** /**
* 设置记录的某个字段值 * 利用__call方法实现一些特殊的Model方法
* 支持使用数据库字段和方法 * @access public
* @access public * @param string $method 方法名称
* @param string|array $field 字段名 * @param array $args 调用参数
* @param string $value 字段值 * @return mixed
* @return boolean */
*/ public function __call($method,$args) {
public function setField($field,$value='') { if(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){
if(is_array($field)) { // 统计查询的实现
$data = $field; $field = isset($args[0])?$args[0]:'*';
}else{ return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method);
$data[$field] = $value; }elseif(strtolower(substr($method,0,5))=='getby') {
} // 根据某个字段获取记录
return $this->save($data); $field = parse_name(substr($method,5));
} $where[$field] = $args[0];
return $this->where($where)->find();
/** }elseif(strtolower(substr($method,0,10))=='getfieldby') {
* 字段值增长 // 根据某个字段获取记录的某个值
* @access public $name = parse_name(substr($method,10));
* @param string $field 字段名 $where[$name] =$args[0];
* @param integer $step 增长值 return $this->where($where)->getField($args[1]);
* @return boolean }elseif(isset($this->_scope[$method])){// 命名范围的单独调用支持
*/ return $this->scope($method,$args[0]);
public function setInc($field,$step=1) { }else{
return $this->setField($field,['exp',$field.'+'.$step]); E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
} return;
}
/** }
* 字段值减少
* @access public /**
* @param string $field 字段 * 设置记录的某个字段
* @param integer $step 减少值 * 支持使用数据库字段和方法
* @return boolean * @access public
*/ * @param string|array $field 字段名
public function setDec($field,$step=1) { * @param string $value 字段值
return $this->setField($field,['exp',$field.'-'.$step]); * @return boolean
} */
public function setField($field,$value='') {
if(is_array($field)) {
/** $data = $field;
* 获取一条记录的某个字段值 }else{
* @access public $data[$field] = $value;
* @param string $field 字段名 }
* @param string $spea 字段数据间隔符号 NULL返回数组 return $this->save($data);
* @return mixed }
*/
public function getField($field,$sepa=null) { /**
$options['field'] = $field; * 字段值增长
$options = $this->_parseOptions($options); * @access public
$field = trim($field); * @param string $field 字段名
if(strpos($field,',')) { // 多字段 * @param integer $step 增长值
if(!isset($options['limit'])){ * @return boolean
$options['limit'] = is_numeric($sepa)?$sepa:''; */
} public function setInc($field,$step=1) {
$resultSet = $this->db->select($options); return $this->setField($field,['exp',$field.'+'.$step]);
if(!empty($resultSet)) { }
$_field = explode(',', $field);
$field = array_keys($resultSet[0]); /**
$key = array_shift($field); * 字段值减少
$key2 = array_shift($field); * @access public
$cols = []; * @param string $field 字段名
$count = count($_field); * @param integer $step 减少值
foreach ($resultSet as $result){ * @return boolean
$name = $result[$key]; */
if(2==$count) { public function setDec($field,$step=1) {
$cols[$name] = $result[$key2]; return $this->setField($field,['exp',$field.'-'.$step]);
}else{ }
$cols[$name] = is_string($sepa)?implode($sepa,$result):$result;
}
} /**
return $cols; * 获取一条记录的某个字段值
} * @access public
}else{ // 查找一条记录 * @param string $field 字段名
// 返回数据个数 * @param string $spea 字段数据间隔符号 NULL返回数组
if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 * @return mixed
$options['limit'] = is_numeric($sepa)?$sepa:1; */
} public function getField($field,$sepa=null) {
$result = $this->db->select($options); $options['field'] = $field;
if(!empty($result)) { $options = $this->_parseOptions($options);
if(true !== $sepa && 1==$options['limit']) return reset($result[0]); $field = trim($field);
foreach ($result as $val){ if(strpos($field,',')) { // 多字段
$array[] = $val[$field]; if(!isset($options['limit'])){
} $options['limit'] = is_numeric($sepa)?$sepa:'';
return $array; }
} $resultSet = $this->db->select($options);
} if(!empty($resultSet)) {
return null; $_field = explode(',', $field);
} $field = array_keys($resultSet[0]);
$key = array_shift($field);
/** $key2 = array_shift($field);
* 字段值延迟增长 $cols = [];
* @access public $count = count($_field);
* @param string $field 字段名 foreach ($resultSet as $result){
* @param integer $step 增长值 $name = $result[$key];
* @param integer $lazyTime 延时时间(s) if(2==$count) {
* @return boolean $cols[$name] = $result[$key2];
*/ }else{
public function setLazyInc($field,$step=1,$lazyTime=0) { $cols[$name] = is_string($sepa)?implode($sepa,$result):$result;
$condition = $this->options['where']; }
if(empty($condition)) { // 没有条件不做任何更新 }
return false; return $cols;
} }
if($lazyTime>0) {// 延迟写入 }else{ // 查找一条记录
$guid = md5($this->name.'_'.$field.'_'.serialize($condition)); // 返回数据个数
$step = $this->lazyWrite($guid,$step,$lazyTime); if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
if(false === $step ) return true; // 等待下次写入 $options['limit'] = is_numeric($sepa)?$sepa:1;
} }
return $this->setField($field,array('exp',$field.'+'.$step)); $result = $this->db->select($options);
} if(!empty($result)) {
if(true !== $sepa && 1==$options['limit']) return reset($result[0]);
/** foreach ($result as $val){
* 字段值延迟减少 $array[] = $val[$field];
* @access public }
* @param string $field 字段名 return $array;
* @param integer $step 减少值 }
* @param integer $lazyTime 延时时间(s) }
* @return boolean return null;
*/ }
public function setLazyDec($field,$step=1,$lazyTime=0) {
$condition = $this->options['where']; /**
if(empty($condition)) { // 没有条件不做任何更新 * 字段值延迟增长
return false; * @access public
} * @param string $field 字段名
if($lazyTime>0) {// 延迟写入 * @param integer $step 增长值
$guid = md5($this->name.'_'.$field.'_'.serialize($condition)); * @param integer $lazyTime 延时时间(s)
$step = $this->lazyWrite($guid,$step,$lazyTime); * @return boolean
if(false === $step ) return true; // 等待下次写入 */
} public function setLazyInc($field,$step=1,$lazyTime=0) {
return $this->setField($field,array('exp',$field.'-'.$step)); $condition = $this->options['where'];
} if(empty($condition)) { // 没有条件不做任何更新
return false;
/** }
* 延时更新检查 返回false表示需要延时 if($lazyTime>0) {// 延迟写入
* 否则返回实际写入的数值 $guid = md5($this->name.'_'.$field.'_'.serialize($condition));
* @access public $step = $this->lazyWrite($guid,$step,$lazyTime);
* @param string $guid 写入标识 if(false === $step ) return true; // 等待下次写入
* @param integer $step 写入步进值 }
* @param integer $lazyTime 延时时间(s) return $this->setField($field,array('exp',$field.'+'.$step));
* @return false|integer }
*/
protected function lazyWrite($guid,$step,$lazyTime) { /**
if(false !== ($value = F($guid))) { // 存在缓存写入数据 * 字段值延迟减少
if(time()>S($guid.'_time')+$lazyTime) { * @access public
// 延时更新时间到了,删除缓存数据 并实际写入数据库 * @param string $field 字段名
S($guid,NULL); * @param integer $step 减少值
S($guid.'_time',NULL); * @param integer $lazyTime 延时时间(s)
return $value+$step; * @return boolean
}else{ */
// 追加数据到缓存 public function setLazyDec($field,$step=1,$lazyTime=0) {
S($guid,$value+$step); $condition = $this->options['where'];
return false; if(empty($condition)) { // 没有条件不做任何更新
} return false;
}else{ // 没有缓存数据 }
S($guid,$step); if($lazyTime>0) {// 延迟写入
// 计时开始 $guid = md5($this->name.'_'.$field.'_'.serialize($condition));
S($guid.'_time',time()); $step = $this->lazyWrite($guid,$step,$lazyTime);
return false; if(false === $step ) return true; // 等待下次写入
} }
} return $this->setField($field,array('exp',$field.'-'.$step));
}
/**
* 得到分表的的数据表名 /**
* @access public * 延时更新检查 返回false表示需要延时
* @param array $data 操作的数 * 否则返回实际写入的数
* @return string * @access public
*/ * @param string $guid 写入标识
public function getPartitionTableName($data=[]) { * @param integer $step 写入步进值
// 对数据表进行分区 * @param integer $lazyTime 延时时间(s)
if(isset($data[$this->partition['field']])) { * @return false|integer
$field = $data[$this->partition['field']]; */
switch($this->partition['type']) { protected function lazyWrite($guid,$step,$lazyTime) {
case 'id': if(false !== ($value = F($guid))) { // 存在缓存写入数据
// 按照id范围分表 if(time()>S($guid.'_time')+$lazyTime) {
$step = $this->partition['expr']; // 延时更新时间到了,删除缓存数据 并实际写入数据库
$seq = floor($field / $step)+1; S($guid,NULL);
break; S($guid.'_time',NULL);
case 'year': return $value+$step;
// 按照年份分表 }else{
if(!is_numeric($field)) { // 追加数据到缓存
$field = strtotime($field); S($guid,$value+$step);
} return false;
$seq = date('Y',$field)-$this->partition['expr']+1; }
break; }else{ // 没有缓存数据
case 'mod': S($guid,$step);
// 按照id的模数分表 // 计时开始
$seq = ($field % $this->partition['num'])+1; S($guid.'_time',time());
break; return false;
case 'md5': }
// 按照md5的序列分表 }
$seq = (ord(substr(md5($field),0,1)) % $this->partition['num'])+1;
break; /**
default : * 得到分表的的数据表名
if(function_exists($this->partition['type'])) { * @access public
// 支持指定函数哈希 * @param array $data 操作的数据
$fun = $this->partition['type']; * @return string
$seq = (ord(substr($fun($field),0,1)) % $this->partition['num'])+1; */
}else{ public function getPartitionTableName($data=[]) {
// 按照字段的首字母的值分表 // 对数据表进行分区
$seq = (ord($field{0}) % $this->partition['num'])+1; if(isset($data[$this->partition['field']])) {
} $field = $data[$this->partition['field']];
} switch($this->partition['type']) {
return $this->getTableName().'_'.$seq; case 'id':
}else{ // 按照id范围分表
// 当设置的分表字段不在查询条件或者数据中 $step = $this->partition['expr'];
// 进行联合查询,必须设定 partition['num'] $seq = floor($field / $step)+1;
$tableName = []; break;
for($i=0;$i<$this->partition['num'];$i++) case 'year':
$tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1); // 按照年份分表
$tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name; if(!is_numeric($field)) {
return $tableName; $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 = [];
for($i=0;$i<$this->partition['num'];$i++)
$tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1);
$tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name;
return $tableName;
}
}
} }