diff --git a/Traits/Think/Model/Extend.php b/Traits/Think/Model/Extend.php index 06d6a118..b39d3b3b 100644 --- a/Traits/Think/Model/Extend.php +++ b/Traits/Think/Model/Extend.php @@ -1,231 +1,261 @@ - -// +---------------------------------------------------------------------- - -namespace Traits\Think\Model; - -trait Extend { - - protected $partition = []; - - /** - * 设置记录的某个字段值 - * 支持使用数据库字段和方法 - * @access public - * @param string|array $field 字段名 - * @param string $value 字段值 - * @return boolean - */ - public function setField($field,$value='') { - if(is_array($field)) { - $data = $field; - }else{ - $data[$field] = $value; - } - return $this->save($data); - } - - /** - * 字段值增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @return boolean - */ - public function setInc($field,$step=1) { - return $this->setField($field,['exp',$field.'+'.$step]); - } - - /** - * 字段值减少 - * @access public - * @param string $field 字段名 - * @param integer $step 减少值 - * @return boolean - */ - public function setDec($field,$step=1) { - return $this->setField($field,['exp',$field.'-'.$step]); - } - - - /** - * 获取一条记录的某个字段值 - * @access public - * @param string $field 字段名 - * @param string $spea 字段数据间隔符号 NULL返回数组 - * @return mixed - */ - public function getField($field,$sepa=null) { - $options['field'] = $field; - $options = $this->_parseOptions($options); - $field = trim($field); - if(strpos($field,',')) { // 多字段 - if(!isset($options['limit'])){ - $options['limit'] = is_numeric($sepa)?$sepa:''; - } - $resultSet = $this->db->select($options); - if(!empty($resultSet)) { - $_field = explode(',', $field); - $field = array_keys($resultSet[0]); - $key = array_shift($field); - $key2 = array_shift($field); - $cols = []; - $count = count($_field); - foreach ($resultSet as $result){ - $name = $result[$key]; - if(2==$count) { - $cols[$name] = $result[$key2]; - }else{ - $cols[$name] = is_string($sepa)?implode($sepa,$result):$result; - } - } - return $cols; - } - }else{ // 查找一条记录 - // 返回数据个数 - if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 - $options['limit'] = is_numeric($sepa)?$sepa:1; - } - $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]; - } - return $array; - } - } - return null; - } - - /** - * 字段值延迟增长 - * @access public - * @param string $field 字段名 - * @param integer $step 增长值 - * @param integer $lazyTime 延时时间(s) - * @return boolean - */ - public function setLazyInc($field,$step=1,$lazyTime=0) { - $condition = $this->options['where']; - if(empty($condition)) { // 没有条件不做任何更新 - return false; - } - if($lazyTime>0) {// 延迟写入 - $guid = md5($this->name.'_'.$field.'_'.serialize($condition)); - $step = $this->lazyWrite($guid,$step,$lazyTime); - if(false === $step ) return true; // 等待下次写入 - } - return $this->setField($field,array('exp',$field.'+'.$step)); - } - - /** - * 字段值延迟减少 - * @access public - * @param string $field 字段名 - * @param integer $step 减少值 - * @param integer $lazyTime 延时时间(s) - * @return boolean - */ - public function setLazyDec($field,$step=1,$lazyTime=0) { - $condition = $this->options['where']; - if(empty($condition)) { // 没有条件不做任何更新 - return false; - } - if($lazyTime>0) {// 延迟写入 - $guid = md5($this->name.'_'.$field.'_'.serialize($condition)); - $step = $this->lazyWrite($guid,$step,$lazyTime); - if(false === $step ) return true; // 等待下次写入 - } - return $this->setField($field,array('exp',$field.'-'.$step)); - } - - /** - * 延时更新检查 返回false表示需要延时 - * 否则返回实际写入的数值 - * @access public - * @param string $guid 写入标识 - * @param integer $step 写入步进值 - * @param integer $lazyTime 延时时间(s) - * @return false|integer - */ - protected function lazyWrite($guid,$step,$lazyTime) { - if(false !== ($value = F($guid))) { // 存在缓存写入数据 - if(time()>S($guid.'_time')+$lazyTime) { - // 延时更新时间到了,删除缓存数据 并实际写入数据库 - S($guid,NULL); - S($guid.'_time',NULL); - return $value+$step; - }else{ - // 追加数据到缓存 - S($guid,$value+$step); - return false; - } - }else{ // 没有缓存数据 - S($guid,$step); - // 计时开始 - S($guid.'_time',time()); - return false; - } - } - - /** - * 得到分表的的数据表名 - * @access public - * @param array $data 操作的数据 - * @return string - */ - public function getPartitionTableName($data=[]) { - // 对数据表进行分区 - 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 = []; - for($i=0;$i<$this->partition['num'];$i++) - $tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1); - $tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name; - return $tableName; - } - } + +// +---------------------------------------------------------------------- + +namespace Traits\Think\Model; + +trait Extend { + + protected $partition = []; + + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + */ + public function __call($method,$args) { + if(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){ + // 统计查询的实现 + $field = isset($args[0])?$args[0]:'*'; + return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method); + }elseif(strtolower(substr($method,0,5))=='getby') { + // 根据某个字段获取记录 + $field = parse_name(substr($method,5)); + $where[$field] = $args[0]; + return $this->where($where)->find(); + }elseif(strtolower(substr($method,0,10))=='getfieldby') { + // 根据某个字段获取记录的某个值 + $name = parse_name(substr($method,10)); + $where[$name] =$args[0]; + return $this->where($where)->getField($args[1]); + }elseif(isset($this->_scope[$method])){// 命名范围的单独调用支持 + return $this->scope($method,$args[0]); + }else{ + E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); + return; + } + } + + /** + * 设置记录的某个字段值 + * 支持使用数据库字段和方法 + * @access public + * @param string|array $field 字段名 + * @param string $value 字段值 + * @return boolean + */ + public function setField($field,$value='') { + if(is_array($field)) { + $data = $field; + }else{ + $data[$field] = $value; + } + return $this->save($data); + } + + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @return boolean + */ + public function setInc($field,$step=1) { + return $this->setField($field,['exp',$field.'+'.$step]); + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param integer $step 减少值 + * @return boolean + */ + public function setDec($field,$step=1) { + return $this->setField($field,['exp',$field.'-'.$step]); + } + + + /** + * 获取一条记录的某个字段值 + * @access public + * @param string $field 字段名 + * @param string $spea 字段数据间隔符号 NULL返回数组 + * @return mixed + */ + public function getField($field,$sepa=null) { + $options['field'] = $field; + $options = $this->_parseOptions($options); + $field = trim($field); + if(strpos($field,',')) { // 多字段 + if(!isset($options['limit'])){ + $options['limit'] = is_numeric($sepa)?$sepa:''; + } + $resultSet = $this->db->select($options); + if(!empty($resultSet)) { + $_field = explode(',', $field); + $field = array_keys($resultSet[0]); + $key = array_shift($field); + $key2 = array_shift($field); + $cols = []; + $count = count($_field); + foreach ($resultSet as $result){ + $name = $result[$key]; + if(2==$count) { + $cols[$name] = $result[$key2]; + }else{ + $cols[$name] = is_string($sepa)?implode($sepa,$result):$result; + } + } + return $cols; + } + }else{ // 查找一条记录 + // 返回数据个数 + if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 + $options['limit'] = is_numeric($sepa)?$sepa:1; + } + $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]; + } + return $array; + } + } + return null; + } + + /** + * 字段值延迟增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @param integer $lazyTime 延时时间(s) + * @return boolean + */ + public function setLazyInc($field,$step=1,$lazyTime=0) { + $condition = $this->options['where']; + if(empty($condition)) { // 没有条件不做任何更新 + return false; + } + if($lazyTime>0) {// 延迟写入 + $guid = md5($this->name.'_'.$field.'_'.serialize($condition)); + $step = $this->lazyWrite($guid,$step,$lazyTime); + if(false === $step ) return true; // 等待下次写入 + } + return $this->setField($field,array('exp',$field.'+'.$step)); + } + + /** + * 字段值延迟减少 + * @access public + * @param string $field 字段名 + * @param integer $step 减少值 + * @param integer $lazyTime 延时时间(s) + * @return boolean + */ + public function setLazyDec($field,$step=1,$lazyTime=0) { + $condition = $this->options['where']; + if(empty($condition)) { // 没有条件不做任何更新 + return false; + } + if($lazyTime>0) {// 延迟写入 + $guid = md5($this->name.'_'.$field.'_'.serialize($condition)); + $step = $this->lazyWrite($guid,$step,$lazyTime); + if(false === $step ) return true; // 等待下次写入 + } + return $this->setField($field,array('exp',$field.'-'.$step)); + } + + /** + * 延时更新检查 返回false表示需要延时 + * 否则返回实际写入的数值 + * @access public + * @param string $guid 写入标识 + * @param integer $step 写入步进值 + * @param integer $lazyTime 延时时间(s) + * @return false|integer + */ + protected function lazyWrite($guid,$step,$lazyTime) { + if(false !== ($value = F($guid))) { // 存在缓存写入数据 + if(time()>S($guid.'_time')+$lazyTime) { + // 延时更新时间到了,删除缓存数据 并实际写入数据库 + S($guid,NULL); + S($guid.'_time',NULL); + return $value+$step; + }else{ + // 追加数据到缓存 + S($guid,$value+$step); + return false; + } + }else{ // 没有缓存数据 + S($guid,$step); + // 计时开始 + S($guid.'_time',time()); + return false; + } + } + + /** + * 得到分表的的数据表名 + * @access public + * @param array $data 操作的数据 + * @return string + */ + public function getPartitionTableName($data=[]) { + // 对数据表进行分区 + 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 = []; + for($i=0;$i<$this->partition['num'];$i++) + $tableName[] = 'SELECT * FROM '.$this->getTableName().'_'.($i+1); + $tableName = '( '.implode(" UNION ",$tableName).') AS '.$this->name; + return $tableName; + } + } } \ No newline at end of file