|
@@ -6,14 +6,209 @@ namespace Qii\Driver\Entity;
|
|
|
use Qii\Driver\Response;
|
|
|
|
|
|
class Base {
|
|
|
- public static $propertys;
|
|
|
+ /**
|
|
|
+ * 字段及值
|
|
|
+ *
|
|
|
+ * @var array $properties
|
|
|
+ */
|
|
|
+ public static $properties;
|
|
|
+ public static $config;
|
|
|
+ /**
|
|
|
+ * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存
|
|
|
+ * @var array $hooker
|
|
|
+ */
|
|
|
+ private $hooker = [];
|
|
|
+ /**
|
|
|
+ * 默认过期时间
|
|
|
+ *
|
|
|
+ * @var int $defaultExpired
|
|
|
+ */
|
|
|
+ private $defaultExpired = 600;
|
|
|
+
|
|
|
public function __construct(){
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认值,仅在新增数据的时候支持,更新不支持
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
public function defaultFieldsValue(){}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 返回 table 的名字,以便分库分表的时候指定使用
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
public function getTable() {
|
|
|
throw new \Exception('请先设置需要保存的数据表');
|
|
|
}
|
|
|
+ /**
|
|
|
+ * set hooker for method
|
|
|
+ * 目前 where 和 order 及 fields 支持
|
|
|
+ * @param $key
|
|
|
+ * @param $hooker
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ protected function setHooker($key, $hooker, $args) {
|
|
|
+ $this->hooker[$key]['func'] = $hooker;
|
|
|
+ $this->hooker[$key]['args'] = $args;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置where hooker
|
|
|
+ * @param object $hooker
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function setWhereHooker($hooker, $args = null) {
|
|
|
+ $this->checkCallable($hooker);
|
|
|
+ $this->setHooker('where', $hooker, $args);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取where条件
|
|
|
+ *
|
|
|
+ * @return array|mixed
|
|
|
+ */
|
|
|
+ public function getWhereHooker() {
|
|
|
+ $where = $this->properties();
|
|
|
+ if($this->hooker['where'] && is_callable($this->hooker['where']['func'])) {
|
|
|
+ $where = call_user_func($this->hooker['where']['func'], $this->properties(), $this->hooker['where']['args']);
|
|
|
+ }
|
|
|
+ return $where;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置order by hooker
|
|
|
+ * @param object $hooker
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function setOrderHooker($hooker, $args = null) {
|
|
|
+ $this->checkCallable($hooker);
|
|
|
+ $this->setHooker('order', $hooker, $args);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置 limit hooker
|
|
|
+ *
|
|
|
+ * @param object $hooker
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function setLimitHooker($hooker, $args = null) {
|
|
|
+ $this->checkCallable($hooker);
|
|
|
+ $this->setHooker('order', $hooker, $args);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 获取limit
|
|
|
+ * @return int|mixed
|
|
|
+ */
|
|
|
+ public function getLimitHooker() {
|
|
|
+ if($this->hooker['limit'] && is_callable($this->hooker['limit']['func'])) {
|
|
|
+ return call_user_func($this->hooker['limit']['func'], [], $this->hooker['limit']['args']);
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置 cache hooker
|
|
|
+ *
|
|
|
+ * @param callable $hooker
|
|
|
+ * @param array| mixed $args
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function setCacheHooker($hooker, $args = null) {
|
|
|
+ $this->checkCallable($hooker);
|
|
|
+ $this->setHooker('cache', $hooker, $args);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取Cache Hooker
|
|
|
+ * @return mixed|null[]
|
|
|
+ */
|
|
|
+ public function getCacheHooker() {
|
|
|
+ if($this->hooker['cache'] && is_callable($this->hooker['cache']['func'])) {
|
|
|
+ return call_user_func($this->hooker['cache']['func'], [], $this->hooker['cache']['args']);
|
|
|
+ }
|
|
|
+ return [null, null, null];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 缓存设置
|
|
|
+ *
|
|
|
+ * @param array $config 缓存配置
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ public function setCacheConfig($config) {
|
|
|
+ if(!is_array($config)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ self::$config['cache'] = $config;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取缓存的配置信息
|
|
|
+ *
|
|
|
+ * @return array
|
|
|
+ */
|
|
|
+ public function getCacheConfig() {
|
|
|
+ if(is_array(self::$config) && isset(self::$config['cache']) && is_array(self::$config['cache'])) {
|
|
|
+ return self::$config['cache'];
|
|
|
+ }
|
|
|
+ return array();
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 如果hooker不可调用即抛出异常
|
|
|
+ *
|
|
|
+ * @param object $hooker
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ private function checkCallable($hooker) {
|
|
|
+ if($hooker && !is_callable($hooker)) {
|
|
|
+ throw new \Exception('Hooker is uncallable');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 设置order by hooker
|
|
|
+ * @param object $hooker
|
|
|
+ * @return void
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function setQueryFieldsHooker($hooker, $args = null) {
|
|
|
+ if(!is_callable($hooker)) {
|
|
|
+ throw new \Exception('Hooker is un callable');
|
|
|
+ }
|
|
|
+ $this->setHooker('fields', $hooker, $args);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过field hooker返回查询的字段
|
|
|
+ *
|
|
|
+ * @return mixed|string
|
|
|
+ */
|
|
|
+ public function getFieldsHooker() {
|
|
|
+ if($this->hooker['fields'] && is_callable($this->hooker['fields']['func'])) {
|
|
|
+ return call_user_func($this->hooker['fields']['func'], [], $this->hooker['fields']['args']);
|
|
|
+ }
|
|
|
+ return '*';
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取指定field的值
|
|
|
+ * @param string $field
|
|
|
+ * @return mixed
|
|
|
+ */
|
|
|
+ public function getFieldVal($field) {
|
|
|
+ if(!preg_match('/^[a-zA-Z]/', $field)) {
|
|
|
+ throw new \Exception('Field is illegal');
|
|
|
+ }
|
|
|
+ $property = $this->entity()->convertToProperty($field);
|
|
|
+ return $this->{$property};
|
|
|
+ }
|
|
|
/**
|
|
|
* 返回使用的db信息
|
|
|
*
|
|
@@ -81,10 +276,30 @@ class Base {
|
|
|
}
|
|
|
return explode($separator, $string, $limit);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加数据验证字段
|
|
|
+ *
|
|
|
+ * $fields = [];
|
|
|
+ * return $this->valid($fields);
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
public function validFieldsForAdd(){
|
|
|
throw new \Exception('请设置插入数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新数据验证字段
|
|
|
+ *
|
|
|
+ * $fields = [];
|
|
|
+ * return $this->valid($fields);
|
|
|
+ *
|
|
|
+ * @return mixed
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
public function validFieldsForUpdate() {
|
|
|
throw new \Exception('请设置更新数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
|
|
|
}
|
|
@@ -136,7 +351,7 @@ class Base {
|
|
|
* @throws \Exception
|
|
|
*/
|
|
|
public function count() {
|
|
|
- return $this->db()->fields(' COUNT(1) as count')->where($this->properties())->selectOne($this->getTable());
|
|
|
+ return $this->db()->fields(' COUNT(1) as count')->where($this->getWhereHooker())->selectOne($this->getTable());
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -148,19 +363,19 @@ class Base {
|
|
|
public function hasProperty($key) {
|
|
|
$class = get_called_class();
|
|
|
$key = $this->entity()->convertToProperty($key);
|
|
|
- if(isset(self::$propertys[$class])) {
|
|
|
- return isset(self::$propertys[$class][$key]);
|
|
|
+ if(isset(self::$properties[$class])) {
|
|
|
+ return isset(self::$properties[$class][$key]);
|
|
|
}
|
|
|
$method = new \ReflectionClass($class);
|
|
|
$properties = $method->getproperties();
|
|
|
- $fields = [];
|
|
|
+ $fields = array();
|
|
|
foreach($properties as $property) {
|
|
|
if($property->isPublic()) {
|
|
|
$name = $property->getName();
|
|
|
$fields[$name] = '';
|
|
|
}
|
|
|
}
|
|
|
- self::$propertys[$class] = $fields;
|
|
|
+ self::$properties[$class] = $fields;
|
|
|
return isset($fields[$key]);
|
|
|
}
|
|
|
|
|
@@ -181,17 +396,21 @@ class Base {
|
|
|
}
|
|
|
return $this;
|
|
|
}
|
|
|
-
|
|
|
/**
|
|
|
* 获取数据
|
|
|
*
|
|
|
- * @return mixed
|
|
|
+ * @return Object
|
|
|
* @throws \Exception
|
|
|
*/
|
|
|
public function get() {
|
|
|
- $info = $this->db()->where($this->properties())->selectRow($this->getTable());
|
|
|
$class = get_called_class();
|
|
|
$obj = new $class();
|
|
|
+ $response = $this->info();
|
|
|
+ if($response->isError()) {
|
|
|
+ return $obj;
|
|
|
+ }
|
|
|
+ $info = $response->getResult()['body'];
|
|
|
+
|
|
|
if(!$info) {
|
|
|
return $obj;
|
|
|
}
|
|
@@ -201,18 +420,33 @@ class Base {
|
|
|
}
|
|
|
return $obj;
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
+ /**
|
|
|
+ * 与get不同的这个返回的是Response
|
|
|
+ * @return mixed|\Qii\Driver\Qii\Driver\Response
|
|
|
+ */
|
|
|
+ public function info() {
|
|
|
+ try{
|
|
|
+ $info = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->selectRow($this->getTable());
|
|
|
+ return Response::Success(static::class .'::'. __FUNCTION__,
|
|
|
+ ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $info]]
|
|
|
+ );
|
|
|
+ }catch (\Exception $e) {
|
|
|
+ return Response::Fail(static::class .'::'. __FUNCTION__,
|
|
|
+ ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => $this->db()->getMessage(), 'body' => []]
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
/**
|
|
|
* 是否相关数据存在
|
|
|
*
|
|
|
* @return bool
|
|
|
*/
|
|
|
public function exist() {
|
|
|
- if(!$this->properties()) {
|
|
|
+ $where = $this->getWhereHooker();
|
|
|
+ if(!$where) {
|
|
|
return false;
|
|
|
}
|
|
|
- return (bool) $this->db()->where($this->properties())->selectOne($this->getTable());
|
|
|
+ return (bool) $this->db()->where($where)->selectOne($this->getTable());
|
|
|
}
|
|
|
/**
|
|
|
* 保存数据
|
|
@@ -279,7 +513,7 @@ class Base {
|
|
|
if($valid->isError()) {
|
|
|
return $valid;
|
|
|
}
|
|
|
- $affectedRows = $this->db()->where($this->properties())->delete($this->getTable());
|
|
|
+ $affectedRows = $this->db()->where($this->getWhereHooker())->delete($this->getTable());
|
|
|
if($this->db()->isError()) {
|
|
|
return Response::FailSave(static::class .'::'. __FUNCTION__,
|
|
|
['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => []]
|
|
@@ -340,10 +574,10 @@ class Base {
|
|
|
);
|
|
|
}
|
|
|
//获取默认值
|
|
|
+ //更新的时候不使用默认值
|
|
|
+ //$values = array_merge($this->getDefaultValue(), $this->properties());
|
|
|
|
|
|
- $values = array_merge($this->getDefaultValue(), $this->properties());
|
|
|
-
|
|
|
- $affectedRows = $this->db()->updateObject($this->getTable(), $values, $primaryKey);
|
|
|
+ $affectedRows = $this->db()->updateObject($this->getTable(), $this->properties(), $primaryKey);
|
|
|
if($this->db()->isError()) {
|
|
|
return Response::FailUpdate(static::class .'::'. __FUNCTION__,
|
|
|
['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
|
|
@@ -397,7 +631,6 @@ class Base {
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
//检查更新的数据是否存在,以主键为主
|
|
|
$exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->getTable());
|
|
|
if($exit === null || $exit === false) {
|
|
@@ -407,9 +640,10 @@ class Base {
|
|
|
}
|
|
|
//获取默认值
|
|
|
|
|
|
- $values = array_merge($this->getDefaultValue(), $this->properties());
|
|
|
+ //更新的时候不使用默认值
|
|
|
+ //$values = array_merge($this->getDefaultValue(), $this->properties());
|
|
|
|
|
|
- $affectedRows = $this->db()->updateObject($this->getTable(), $values, $primaryKey);
|
|
|
+ $affectedRows = $this->db()->updateObject($this->getTable(), $this->properties(), $primaryKey);
|
|
|
if($this->db()->isError()) {
|
|
|
return Response::FailUpdate(static::class .'::'. __FUNCTION__,
|
|
|
['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
|
|
@@ -493,7 +727,7 @@ class Base {
|
|
|
foreach ($orderBy as $key => $value) {
|
|
|
$orderBy[$key] = 'DESC';
|
|
|
}
|
|
|
- $row = $this->db()->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
|
|
|
+ $row = $this->db()->fields($this->getFieldsHooker())->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
|
|
|
if($this->db()->isError()) {
|
|
|
return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
|
|
|
}
|
|
@@ -513,12 +747,28 @@ class Base {
|
|
|
if(!$this->initPages($data, $count, $page, $pageSize)) {
|
|
|
return $data;
|
|
|
}
|
|
|
- $order = $this->getOrderBy();
|
|
|
- $data['lists'] = $this->db()->where($this->properties())->orderBy($order)->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
|
|
|
-
|
|
|
+ $data['lists'] = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
|
|
|
return $data;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取所有数据,这里不分页,但支持限条数,通过 setLimitHooker来实现
|
|
|
+ * $this->setLimitHooker(function(){
|
|
|
+ * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的
|
|
|
+ * });
|
|
|
+ * @return mixed
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function listAll() {
|
|
|
+ $limit = $this->getLimitHooker();
|
|
|
+ if(empty($limit) || !is_array($limit)) {
|
|
|
+ return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->selectRows($this->getTable());
|
|
|
+ }
|
|
|
+ if(count($limit) == 1) {
|
|
|
+ return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0])->selectRows($this->getTable());
|
|
|
+ }
|
|
|
+ return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0], $limit[1])->selectRows($this->getTable());
|
|
|
+ }
|
|
|
/**
|
|
|
* 验证相关字段
|
|
|
*
|
|
@@ -686,6 +936,9 @@ class Base {
|
|
|
public function getOrderBy() {
|
|
|
$order = array();
|
|
|
$orderBy = $this->orderBy();
|
|
|
+ if(isset($this->hooker['order']) && is_callable($this->hooker['order']['func'])) {
|
|
|
+ $orderBy = call_user_func($this->hooker['order']['func'], $order, $this->hooker['order']['args']);
|
|
|
+ }
|
|
|
foreach ($orderBy as $key => $val) {
|
|
|
$field = $this->entity()->convertToField($key);
|
|
|
$val = strtoupper($val);
|
|
@@ -743,14 +996,64 @@ class Base {
|
|
|
$data['pages']['pageSize'] = (int) $pageSize;
|
|
|
return true;
|
|
|
}
|
|
|
+ /**
|
|
|
+ * response as object
|
|
|
+ *
|
|
|
+ * @return mixed|\Qii\Driver\Qii\Driver\Response
|
|
|
+ */
|
|
|
+ public function response() {
|
|
|
+ $properties = $this->properties();
|
|
|
+ if(!$properties) {
|
|
|
+ return Response::Fail(static::class .'::'. __FUNCTION__,
|
|
|
+ ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => '未找到相关数据', 'body' => []]
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
+ return Response::Success(static::class .'::'. __FUNCTION__,
|
|
|
+ ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $properties]]
|
|
|
+ );
|
|
|
+
|
|
|
+ }
|
|
|
/**
|
|
|
- * 默认转发到 db model上
|
|
|
+ * 方法名 + ByCache/ByRedis/ByMemcache/会将结果缓存,这里使用的缓存类是通过hooker设置的,否则使用对应的
|
|
|
+ * ByClean 则会移除当前缓存, 默认转发到 db model上
|
|
|
* @param string $method method
|
|
|
* @param array $args
|
|
|
* @return mixed
|
|
|
*/
|
|
|
public function __call($method, $args) {
|
|
|
+ $cache = '';
|
|
|
+ preg_match("/(byredis|bymemcache|bycache|byclean)$/i", $method, $matches);
|
|
|
+ if($matches && count($matches) > 0) {
|
|
|
+ $cache = strtolower($matches[0]);
|
|
|
+ }
|
|
|
+ if($cache && in_array($cache, ['bymemcache', 'byredis', 'bycache', 'byclean'])){
|
|
|
+ list($func, $cacheID, $config) = $this->getCacheHooker();
|
|
|
+ $policy = $this->getCacheConfig();
|
|
|
+ if($func == null) {
|
|
|
+ $func = $cache == 'bymemcache' ? $this->db()->setCache('memcached', $policy) : $this->db()->setCache('redis', $policy);
|
|
|
+ }
|
|
|
+ $method = substr($method, 0, strlen($cache) * -1);
|
|
|
+ if (method_exists($this, $method)) {
|
|
|
+ $key = get_called_class() .':'. $method .':'. substr(md5(serialize($this->properties())), -16);
|
|
|
+ if($cache == 'byclean') {
|
|
|
+ return $func->del($key);
|
|
|
+ }
|
|
|
+ if(is_object($func) && method_exists($func, 'get')) {
|
|
|
+ $res = $func->get($key);
|
|
|
+ if ($res) {
|
|
|
+ return unserialize($res);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $expiredAt = isset($config['life_time']) ? (int) $config['life_time'] :
|
|
|
+ (isset($policy['life_time']) ? $policy['life_time'] : $this->defaultExpired);
|
|
|
+ $res = call_user_func_array(array($this, $method), $args);
|
|
|
+ $str = serialize($res);
|
|
|
+ $func->set($key, $str, ['life_time' => $config['life_time']]);
|
|
|
+ return $res;
|
|
|
+ }
|
|
|
+ }
|
|
|
return call_user_func_array(array($this->db(), $method), $args);
|
|
|
}
|
|
|
}
|