alias = $name; return $this; } /** * 预处理表名 * * @return mixed|string * @throws \Exception */ public function prepareTable() { if($this->alias == '') { return $this->getTable(); } return $this->getTable() . ' ' . $this->alias; } /** * set hooker for method * 目前 where 和 order 及 fields 支持 * @param $key * @param $hooker * @return $this */ protected function setHooker($key, $hooker, $args) { $this->hooker[$key]['func'] = $hooker; $this->hooker[$key]['args'] = $args; return $this; } /** * Or Hooker * @param $hooker * @param $args * @return $this * @throws \Exception */ public function setOrHooker($hooker, $args = null){ $this->checkCallable($hooker); if(!isset($this->hooker['or'])) { $this->hooker['or'] = []; } $this->hooker['or'][] = ['func' => $hooker, 'args' => $args]; return $this; } /** * 获取or条件 * * @return array */ public function getOrHooker() { $or = []; if(isset($this->hooker['or'])) { foreach ($this->hooker['or'] as $value) { if(is_callable($value['func'])) { $or[] = call_user_func($value['func'], [], $value['args']); } } } return $or; } /** * 设置where hooker * @param object $hooker * @return $this * @throws \Exception */ public function setWhereHooker($hooker, $args = null) { $this->checkCallable($hooker); $this->setHooker('where', $hooker, $args); return $this; } /** * 获取where条件 * * @return array|mixed */ public function getWhereHooker() { $where = $this->properties(); if(isset($this->hooker['where']) && is_callable($this->hooker['where']['func'])) { if($this->alias != ""){ foreach ($where as $key => $value) { if(!preg_match("/{$this->alias}\.{$key}/", $key)) { $where[$this->alias .'.'. $key] = $value; unset($where[$key]); } } } $where = call_user_func($this->hooker['where']['func'], $where, $this->hooker['where']['args']); } return $where; } /** * join hooker * @param \Closure $hooker * @param mix $args * @return $this * @throws \Exception */ public function setJoinHooker($hooker, $args = null) { $this->checkCallable($hooker); if(!isset($this->hooker['join'])) { $this->hooker['join'] = []; } $this->hooker['join'][] = ['func' => $hooker, 'args' => $args]; return $this; } /** * 获取 join hooker * @return array */ public function getJoinHooker() { $join = []; if(isset($this->hooker['join'])) { foreach ($this->hooker['join'] as $value) { if(is_callable($value['func'])) { $join[] = call_user_func($value['func'], [], $value['args']); } } } return $join; } /** * 设置order by hooker * @param object $hooker * @return $this * @throws \Exception */ public function setOrderHooker($hooker, $args = null) { $this->checkCallable($hooker); $this->setHooker('order', $hooker, $args); return $this; } /** * 设置 limit hooker * * @param object $hooker * @return $this * @throws \Exception */ public function setLimitHooker($hooker, $args = null) { $this->checkCallable($hooker); $this->setHooker('order', $hooker, $args); return $this; } /** * 获取limit * @return int|mixed */ public function getLimitHooker() { if(isset($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 $this * @throws \Exception */ public function setCacheHooker($hooker, $args = null) { $this->checkCallable($hooker); $this->setHooker('cache', $hooker, $args); return $this; } /** * 获取Cache Hooker * @return mixed|null[] */ public function getCacheHooker() { if(isset($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(); } /** * 设置关联子查询 * * @param $hooker [关联对象, 返回数据key, hasOne/hasMany, 主键, rel 主键] * @params $args * @return $this * @throws \Exception */ public function setWith($hooker, $args = null){ $this->checkCallable($hooker); if(!isset($this->hooker['with'])) { $this->hooker['with'] = []; } $this->hooker['with'][] = ['func' => $hooker, 'args' => $args]; return $this; } /** * 获取with hooker * * @return array */ public function getWithHooker() { $callback = []; if(isset($this->hooker['with'])) { foreach ($this->hooker['with'] AS $hooker) { if(is_callable($hooker['func'])) { $callback[] = call_user_func($hooker['func'], [], $hooker['args']); } } } return $callback; } /** * 如果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(isset($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信息 * * @return \Qii\Driver\Base */ public function db() { return _loadClass('\Qii\Driver\Model'); } /** * 返回 entity 信息 * @return mixed */ public function entity() { return _loadClass('\Qii\Driver\Entity\Entity'); } /** * 返回所有 Fields * @return array */ public function fields() { return []; } /** * 设置唯一字段 * * @param mixed $uniqKeys 唯一字段 * @return void */ public function setUniqKeys($uniqKeys) { self::$uniqKeys = $uniqKeys; } /** * unique (unique 如果是 array 则表示 联合唯一,如果是string则是任意唯一,多个单独唯一使用字符串以逗号隔开, 主键除外) * * @return mixed * @throws \Exception */ public function uniqueKey(){ if(self::$uniqKeys !== null) { return self::$uniqKeys; } throw new \Exception('请设置唯一值'); } /** * 设置主键 * * @param mixed $privateKeys * @return void */ public function setPricateKey($privateKeys){ self::$privateKeys = $privateKeys; } /** * 主键 * * @return mixed * @throws \Exception */ public function primaryKey(){ if(self::$privateKeys !== null) { return self::$privateKeys; } throw new \Exception('请设置主键'); } /** * 设置排除项 * @param array $exclude * @return void */ public function setExclude($exclude) { if(!is_array($exclude)) { return; } self::$exclude = $exclude; } /** * 保存数据的时候,验证唯一需要排除的值,此处仅支持,单个或联合排除,不支持单个异或排除 * * @return array */ public function exclude(){ if(self::$exclude !== null) { return self::$exclude; } return array(); } /** * order by * @return string[] */ public function orderBy() { return array(); } /** * explode string * @param string $separator 分隔符 * @param string $string 分割的字符串 * @param int $limit 是否 * @return array|false|string[] */ public function explode($separator, $string, $limit = PHP_INT_MAX) { if($string == '') { return []; } 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的属性'); } /** * 设置需要更新为空的字段列表 * * @param $fields * @return $this */ public function setNull($fields) { $tableFields = $this->fields(); foreach ($fields AS $field) { if(!in_array($field, $tableFields)) { continue; } $field = $this->entity()->convertToProperty($field); $this->nullFields[] = $field; } return $this; } /** * 获取不为空的属性 * * @return array */ public function properties() { $class = get_called_class(); $method = new \ReflectionClass($class); $properties = $method->getproperties(); $fields = []; foreach($properties as $property) { if($property->isPublic() && !$property->isStatic()) { $name = $property->getName(); if(!isset($this->$name)) { continue; } $field = $this->entity()->convertToField($name); $fields[$field] = $this->$name; } } if(count($this->nullFields) > 0) { foreach ($this->nullFields AS $field) { $field = $this->entity()->convertToField($field); $fields[$field] = null; } } return $fields; } /** * 获取默认值 * * @return array */ public function getDefaultValue() { $default = []; $defaultValue = $this->defaultFieldsValue(); if(!$defaultValue || !is_array($defaultValue) || count($defaultValue) == 0) { return $default; } foreach ($defaultValue as $key => $value) { $field = $this->entity()->convertToField($key); $default[$field] = $value; } return $default; } /** * 获取总行数 * * @return mixed * @throws \Exception */ public function count() { $query = $this->createQuery(); $query->fields(' COUNT(1) as count'); return $query->selectOne($this->prepareTable()); } /** * 检查是否有对应的属性 * * @param $key * @return bool * @throws \ReflectionException */ public function hasProperty($key) { $class = get_called_class(); $key = $this->entity()->convertToProperty($key); if(isset(self::$properties[$class])) { return isset(self::$properties[$class][$key]); } $method = new \ReflectionClass($class); $properties = $method->getproperties(); $fields = array(); foreach($properties as $property) { if($property->isPublic()) { $name = $property->getName(); $fields[$name] = ''; } } self::$properties[$class] = $fields; return isset($fields[$key]); } /** * bind value * @param $values * @return $this * @throws \ReflectionException */ public function bindValues($values) { if(!is_array($values)) { return $this; } foreach ($values as $key => $value) { $property = $this->entity()->convertToProperty($key); if($this->hasProperty($key)) { $this->$property = $value; } } return $this; } /** * 回去当前执行的sql * * @return string */ public function getCalledSQL() { return $this->calledSQL; } /** * 获取数据 * * @return Object * @throws \Exception */ public function get() { $class = get_called_class(); $obj = new $class(); $response = $this->info(); if($response->isError()) { $obj->__Error = $response->getResult(); return $obj; } $info = $response->getResult()['body']; if(!$info) { return $obj; } foreach ($info as $key => $val) { $key = $this->entity()->convertToProperty($key); $obj->$key = $val; } return $obj; } /** * get 是否返回了错误 * * @return bool */ public function isError() { if(isset($this->__Error)) { return true; } return false; } /** * get 返回的code * * @return int */ public function getCode() { if(!$this->isError()) { return 0; } return isset($this->__Error['code']) ? $this->__Error['code'] : 0; } /** * get 返回的msg * * @return string */ public function getMessage() { if(!$this->isError()) { return ''; } return isset($this->__Error['msg']) ? $this->__Error['msg'] : ''; } /** * 与get不同的这个返回的是Response * * @return mixed|Response */ public function info() { try{ $query = $this->createQuery(); $info = $query->selectRow($this->prepareTable()); $this->calledSQL = $query->modelSQL; if($info) $this->withRow($info); 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' =>$e->getMessage(), 'body' => []], 'message' => $e->getMessage() ] ); } } /** * 将表对象对应的字段转换成array * * @return array */ public function toArray($returnFields = false) { $arr = []; $method = new \ReflectionClass($this); $vars = []; foreach ($this as $k => $t) { if(preg_match("/^[A-Z]+/", $k)) { $vars[] = $k; } } $fields = $this->fields(); if(count($fields)) { if($returnFields) { foreach ($fields as $key => $value) { $arr['__fields__'][$key] = $value; } } foreach ($vars as $extra) { if(!isset($arr[$extra])) { $field = $this->entity()->convertToField($extra); if(isset($this->$extra)) $arr[$field] = $this->$extra; } } return $arr; } $properties = $method->getproperties(); foreach($properties as $property) { if($property->isPublic() && !$property->isStatic()) { $name = $property->getName(); $field = $this->entity()->convertToField($name); if(isset($this->$name)) $arr[$field] = $this->$name; continue; } //移除非public的变量 $i = array_search($property->getName(), $vars); if($i !== false) { array_splice($vars, $i, 1); } } foreach ($vars as $extra) { if(!isset($arr[$extra])) { $field = $this->entity()->convertToField($extra); if(isset($this->$extra)) $arr[$field] = $this->$extra; } } return $arr; } /** * 是否相关数据存在 * * @return bool */ public function exist() { $where = $this->getWhereHooker(); if(!$where) { return false; } return (bool) $this->db()->where($where)->selectOne($this->prepareTable()); } /** * 保存数据 * * @return mixed|Qii\Driver\Response * @throws \Exception */ public function add() { $valid = $this->validFieldsForAdd(); if($valid->isError()) { return $valid; } //如果设置了 unique 就先验证唯一性 list($uniqueWhere, $uniqueOr, $exclude, $primary) = $this->condition(); unset($exclude); //如果 $where $or 为空,看看主键是否有设置值,有设置值说明验证主键就可以,反之设置了主键,主键值设置了,只验证主键就可以了 if(count($primary) > 0) { $exist = $this->db()->limit(1)->where($primary)->selectRow($this->prepareTable()); if($exist) { return Response::Exist(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist], 'message' => '数据已经存在' ] ); } } if(count($uniqueWhere) > 0 || count($uniqueOr) > 0) { $exist = $this->db()->limit(1)->where($uniqueWhere)->orTerms($uniqueOr)->selectRow($this->prepareTable()); if($exist) { return Response::Exist(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist], 'message' => '数据已存在' ] ); } } $values = array_merge($this->getDefaultValue(), $this->properties()); $res = $this->db()->insertObject($this->prepareTable(), $values); if($this->db()->isError()) { return Response::FailSave(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => []], 'message' => $this->db()->getMessage() ]); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => '添加成功', 'body' => $res] ] ); } /** * 删除指定数据 * * @return false|Response */ public function remove() { if(!$this->properties()) { return Response::Fail(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => '未指定删除目标', 'body' => []], 'message' => '未指定删除目标' ] ); } $properties = []; foreach ($this->properties() as $key => $val) { $properties[] = $this->entity()->convertToProperty($key); } $valid = $this->valid($properties); if($valid->isError()) { return $valid; } $query = $this->createQuery(); $affectedRows = $query->delete($this->prepareTable()); if($this->db()->isError()) { return Response::FailSave(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => []], 'message' => $this->db()->getMessage() ] ); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => '删除成功,总共删除了'. $affectedRows . '条记录', 'body' => $affectedRows], 'message' => '删除成功,总共删除了'. $affectedRows . '条记录' ] ); } /** * 更新 * * @return mixed | Response * @throws \Exception */ public function update(){ $valid = $this->validFieldsForUpdate(); if($valid->isError()) { return $valid; } //检查是否有重复的 list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition(); // 检查 unique 是否已经存在相关数据 $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey); $diffOr = array_diff_assoc($uniqueOr, $primaryKey); /*print_r( [ 'where' => $uniqueWhere, 'or' => $uniqueOr, 'exclude' => $exclude, 'primary' => $primaryKey ] );*/ /*$diffExclude = array_diff_assoc($exclude, $primaryKey); print_r( [ 'diffWhere' => $diffWhere, 'diffOr' => $diffOr, 'diffExclude' => $diffExclude ] );*/ if(count($diffWhere) > 0 || count($diffOr) > 0) { $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->prepareTable()); if($unique) { return Response::Exist(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]] ); } } //检查更新的数据是否存在,以主键为主 $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->prepareTable()); if($exit === null || $exit === false) { return Response::NotExist(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::DOES_NOT_EXIST, 'msg' => '未找到相关记录', 'body' => []]] ); } //获取默认值 //更新的时候不使用默认值 //$values = array_merge($this->getDefaultValue(), $this->properties()); $affectedRows = $this->db()->updateObject($this->prepareTable(), $this->properties(), $primaryKey); if($this->db()->isError()) { return Response::FailUpdate(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []] ] ); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => '数据更新成功,总共更新了'. $affectedRows . '条记录', 'body' => $affectedRows], 'message' => '数据更新成功,总共更新了'. $affectedRows . '条记录' ] ); } /** * 更新字段 * * @return mixed| Response * @throws \Exception */ public function updateFields() { $properties = $this->properties(); $fields = $this->entity()->convertToProperties(array_keys($properties)); $valid = $this->valid($fields); if($valid->isError()) { return $valid; } list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition(); /*print_r( [ 'where' => $uniqueWhere, 'or' => $uniqueOr, 'exclude' => $exclude, 'primary' => $primaryKey ] );*/ // 检查 unique 是否已经存在相关数据 $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey); $diffOr = array_diff_assoc($uniqueOr, $primaryKey); /*$diffExclude = array_diff_assoc($exclude, $primaryKey); print_r( [ 'diffWhere' => $diffWhere, 'diffOr' => $diffOr, 'diffExclude' => $diffExclude ] );*/ if(count($diffWhere) > 0 || count($diffOr) > 0) { $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->prepareTable()); if($unique) { return Response::Exist(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]] ); } } //检查更新的数据是否存在,以主键为主 $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->prepareTable()); if($exit === null || $exit === false) { return Response::NotExist(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DOES_NOT_EXIST, 'msg' => '未找到相关记录', 'body' => []], 'message' => '未找到相关记录' ] ); } //获取默认值 //更新的时候不使用默认值 //$values = array_merge($this->getDefaultValue(), $this->properties()); $affectedRows = $this->db()->updateObject($this->prepareTable(), $this->properties(), $primaryKey); if($this->db()->isError()) { return Response::FailUpdate(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []], 'message' => $this->db()->getMessage() ] ); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => '数据更新成功', 'body' => $affectedRows] ] ); } /** * 增加或减少某一个字段的值 * * @return mixed|Response * @throws \Exception */ public function incr() { list($where, $or, $exclude, $primary) = $this->condition(); unset($where, $or, $exclude); $property = $this->properties(); $incr = []; foreach ($property as $key => $value) { if(!is_numeric($value)) { continue; } $incr[$key] = $value; } $diff = array_diff_assoc($incr, $primary); if(count($diff) == 0) { return Response::Fail(self::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_FAIL, 'msg' => 'INCR 参数错误'], 'message' => 'INCR 参数错误', ] ); } $sets = []; foreach ($diff as $key => $val) { if($val == 0) { $sets[$key] = $val; }else if($val > 0) { $sets[$key . ':plus'] = $val; }else if($val < 0) { $sets[$key . ':minus'] = $val * -1; } } $affectedRows = $this->db()->set($sets)->where($primary)->update($this->prepareTable()); if($this->db()->isError()) { return Response::FailUpdate(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []], 'message' => $this->db()->getMessage() ] ); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'INCR 成功', 'body' => $affectedRows] ] ); } /** * 获取fields * * @return false|mixed|string|string[] */ public function getFields() { $fields = $this->getFieldsHooker(); if(!is_array($fields)) { $fields = explode(',', str_replace(" ", "", $fields)); } return $fields; } /** * 获取where及or条件并创建查询条件 * * @return \Qii\Driver\Base */ public function createQuery($fields = []) { if(empty($fields)) { $fields = $this->getFields(); } $query = $this->db()->fields($fields)->where($this->getWhereHooker())->fetchSql($this->calledSQL); $or = $this->getOrHooker(); foreach ($or as $v) { $query = $query->orTerms($v); } $join = $this->getJoinHooker(); foreach ($join as $j) { $query = $query->join($j); } return $query; } /** * 获取第一条数据 * * @return mixed|Response * @throws \Exception */ public function first() { $orderBy = $this->getOrderBy(); foreach ($orderBy as $key => $value) { $orderBy[$key] = 'ASC'; } $query = $this->createQuery(); $row = $query->limit(1)->orderBy($orderBy)->selectRow($this->prepareTable()); if($this->db()->isError()) { return Response::Fail(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败' . $this->db()->getMessage(), 'body' => []], 'message' => $this->db()->getMessage() ] ); } if($row) { $this->withRow($row); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'Query 成功', 'body' => $row] ] ); } /** * 获取第一条数据 * * @return mixed|Response * @throws \Exception */ public function last() { $orderBy = $this->getOrderBy(); foreach ($orderBy as $key => $value) { $orderBy[$key] = 'DESC'; } $query = $this->createQuery(); $row = $query->orderBy($orderBy)->limit(1)->selectRow($this->prepareTable()); $this->calledSQL = $query->modelSQL; if($this->db()->isError()) { return Response::Fail(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []], 'messsage' => $this->db()->getMessage() ] ); } if($row) { $this->withRow($row); } return Response::Success(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'Query 成功', 'body' => $row] ] ); } /** * 附带一行数据 * * @param $row * @return void */ public function withRow(&$row) { if(!is_array($row)) { return; } $hooks = $this->getWithHooker(); foreach ($hooks as $with) { if(!is_array($with) || count($with) < 5 || !is_object($with[0])) { continue; } $callable = $with[0]; $relField = $with[1]; $method = $with[2]; $foreignKey = $with[3]; $associationForeignkey = $with[4]; $fields = []; if(count($with) >= 6) { $fields = $with[5]; } $row[$relField] = []; if(!isset($row[$foreignKey])) { continue; } $params = [ $associationForeignkey, [$associationForeignkey => $row[$foreignKey]], $fields ]; $res = call_user_func_array(array($callable, $method), $params); if($res && isset($res[$row[$foreignKey]])) { $row[$relField] = $res[$row[$foreignKey]]; } } } /** * 返回游标 * * @param int | null $page 页码 * @param int | null $pageSize 页大小 * @return mixed * @throws \Qii\Exceptions\InvalidParams */ public function rs($page = null, $pageSize = null) { $query = $this->createQuery()->orderBy($this->getOrderBy()); if($page && $pageSize) { $query->limit($page, $pageSize); } return $query->rs($this->prepareTable()); } /** * listts * @param int $page 页码 * @param int $pageSize 页大小 * @return array * @throws \Exception */ public function lists($page = 1, $pageSize = 20) { $count = $this->count(); if(!$this->initPages($data, $count, $page, $pageSize)) { return $data; } $query = $this->createQuery(); $lists = $query->orderBy($this->getOrderBy())->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->prepareTable()); $this->withList($lists); $data['lists'] = $lists; return $data; } /** * 附带一行数据 * * @param array $lists * @return void */ public function withList(&$lists) { if(!is_array($lists)) { return; } $hooks = $this->getWithHooker(); foreach ($hooks as $with) { if(!is_array($with) || count($with) < 5 || !is_object($with[0])) { continue; } $callable = $with[0]; $relField = $with[1]; $method = $with[2]; $foreignKey = $with[3]; $associationForeignkey = $with[4]; $fields = []; if(count($with) == 6) { $fields = $with[5]; } $rel = []; foreach ($lists as $v1) { if(isset($v1[$foreignKey])) { $rel[] = $v1[$foreignKey]; } } // [rel key, where] $params = [ $associationForeignkey, [$associationForeignkey . ':in' => array_unique($rel)], $fields ]; $res = call_user_func_array(array($callable, $method), $params); foreach ($lists as &$v) { if(isset($res[$v[$foreignKey]])) { $v[$relField] = $res[$v[$foreignKey]]; } } } } /** * 关联查询 has one * params[rel_key, where, fields] * * @return array * @throws \Exception */ public function hasOne() { $args = func_get_args(); $relKey = $args[0]; $fields = []; if(count($args) == 3 && !empty($args[2])) { $fields = $args[2]; if(!in_array($relKey, $fields)) $fields[] = $relKey; } if(count($fields) == 0) { $fields = $this->getFields(); } if(count($this->getFields()) > 0 && !in_array($relKey, $this->getFields())) { $fields[] = $relKey; } $fields = array_unique($fields); $query = $this->createQuery($fields); $res = $query->where($args[1])->rs($this->prepareTable()); $rows = []; foreach($res as $v) { $rows[$v[$relKey]] = $v; } return $rows; } /** * 关联查询 * params[rel_key, where, fields] * * @return array * @throws \Exception */ public function hasMany() { $args = func_get_args(); $relKey = $args[0]; $fields = []; if(count($args) == 3 && !empty($args[2])) { $fields = $args[2]; if(!in_array($relKey, $fields)) $fields[] = $relKey; } if(count($fields) == 0) { $fields = $this->getFields(); } if(count($this->getFields()) > 0 && !in_array($relKey, $this->getFields())) { $fields[] = $relKey; } if(array_search('*', $fields) !== false) { $fields = ['*']; } $query = $this->createQuery($fields); $res = $query->where($args[1])->rs($this->prepareTable()); $rows = []; foreach($res as $v) { $rows[$v[$relKey]][] = $v; } return $rows; } /** * 获取所有数据,这里不分页,但支持限条数,通过 setLimitHooker来实现 * $this->setLimitHooker(function(){ * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的 * }); * @return mixed * @throws \Exception */ public function listAll() { $limit = $this->getLimitHooker(); $query = $this->createQuery()->orderBy($this->getOrderBy()); if(empty($limit) || !is_array($limit)) { $list = $query ->selectRows($this->prepareTable()); $this->withList($list); return $list; } if(count($limit) == 1) { $list = $query->limit($limit[0])->selectRows($this->prepareTable()); $this->withList($list); return $list; } $list = $query ->limit($limit[0], $limit[1])->selectRows($this->prepareTable()); $this->withList($list); return $list; } /** * 验证相关字段 * * @param array $fields 字段列表 * @return Response */ public function valid($fields = array()) { if(!is_array($fields)) { return Response::FailValidate(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => '字段必须是数组', 'fields' => []], 'message' => '字段必须是数组' ] ); } $rules = $this->rules(); $result = []; $invalidKey = []; $values = array_merge($this->getDefaultValue(), $this->properties()); foreach ($rules as $valid => $rule) { foreach ($rule as $val) { $key = $this->entity()->convertToProperty($val[0]); if(!in_array($key, $fields)) { continue; } $field = $this->entity()->convertToField($key); $verify = new Verify($val[0], $values[$field] ?? null, strtolower($valid), $val[1], $val[2] ?? ''); $res = $verify->valid(); if($res->isError()) { $result[] = $res->getResult() .',获取的是'. ($values[$field] ?? '空'); if(!in_array($field, $invalidKey)) { $invalidKey[] = $field; } } } } if(count($result) > 0) { return Response::FailValidate(static::class .'::'. __FUNCTION__, [ '_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => $result, 'fields' => $invalidKey], 'message' => join("\n", $result) ] ); } return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]); } /** * 合并多个 valid 结果 $this->valids($this->valid(['Uid']), $this->valid(['Nickname', 'Email'])); * @param ... * @return mixed|Response * @throws \Exception */ public function valids() { $validArr = func_get_args(); if(count($validArr) == 0) { return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]); } if(count($validArr) == 1) { return $validArr[0]; } $invalid = array(); $invalid['message'] = array(); $invalid['fields'] = array(); foreach ($validArr as $valid) { if(!($valid instanceof Response)) { throw new \Exception('验证结果类型必须是\Qii\Driver\Response类型'); } if($valid->isError()) { $result = $valid->getResult(); $invalid['message'] = array_merge($invalid['message'], $result['message']); $invalid['fields'] = array_merge($invalid['fields'], $result['fields']);; } } if(count($invalid['message']) > 0) { return Response::Fail(static::class .'::'. __FUNCTION__, [ '_result' => ['message' => $invalid['message'], 'fields' => $invalid['fields']], 'message' => $invalid['message'] ]); } return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]); } /** * unique 条件 * * @return array[] * @throws \Exception */ protected function uniqueCondition() { $uniqueWhere = []; $uniqueOr = []; $unique = $this->uniqueKey(); $useWhere = true; if(!is_array($unique)) { $unique = $this->explode(',', $unique); $useWhere = false; if(count($unique) == 1) { $useWhere = true; } } foreach ($unique as $key) { $key = $this->entity()->convertToField($key); $property = $this->entity()->convertToProperty($key); if($useWhere) { $uniqueWhere[$key] = $this->$property; }else{ $uniqueOr[$key] = $this->$property; } } return [$uniqueWhere, $uniqueOr]; } /** * exclude Condition * * @return array */ protected function excludeCondition() { if(is_array($this->exclude())) { $exclude = $this->exclude(); }else{ $exclude = (array) $this->explode(',', $this->exclude()); } $excludeCondition = []; foreach ($exclude as $key) { $field = $this->entity()->convertToField($key); $property = $this->entity()->convertToProperty($key); $excludeCondition[$field] = $this->$property; } return $excludeCondition; } /** * 主键 * * @return array * @throws \Exception */ protected function primaryCondition(){ $primary = array(); $primaryKey = $this->primaryKey(); if(!is_array($primaryKey)) { $primaryKey = $this->explode(',', $primaryKey); } foreach ($primaryKey as $key) { $key = $this->entity()->convertToProperty($key); $field = $this->entity()->convertToField($key); $value = $this->$key; $primary[$field] = $value; } return $primary; } /** * 获取查询条件 * * @return array [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition] */ public function condition() { //如果设置了 unique 就先验证唯一性,保存的时候验证 uniqueKey,更新的时候验证uniqueKey并排primaryKey //保存数据的时候验证uniqueKey;更新时验证 uniqueKey,并且排除 primaryKey,如果uniqueKey == primaryKey则不去做唯一性验证 list($uniqueWhere, $uniqueOr) = $this->uniqueCondition(); $excludeCondition = $this->excludeCondition(); $primaryCondition = $this->primaryCondition(); return [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition]; } /** * 获取order by * * @return array */ 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); if($this->alias != "" && strpos($field, '.') === false) { $field = $this->alias . '.' . $field; } $val = strtoupper($val); if(!in_array($val, array('DESC', 'ASC'))) { continue; } $order[$field] = $val; } return $order; } /** * 初始化page * @param array $data 返回的数据 * @param int $count 总数量 * @param int $page 页码 * @param int $pageSize 显示数量 * @param int $pagination 分页显示最多页码 * @return bool */ public function initPages(&$data, $count, $page, $pageSize = 20, $pagination = 5) { $page = max(1, $page); $pageSize = max(1, $pageSize); $data['start'] = 0; $data['pages'] = array('total' => 0, 'currentPage' => 0, 'totalPage' => 0); $data['pages']['total'] = (int) ($count ? $count : 0); $data['pages']['currentPage'] = $page; $data['pages']['totalPage'] = ceil($data['pages']['total'] / $pageSize); $data['lists'] = array(); if ($data['pages']['currentPage'] > $data['pages']['totalPage']) { return false; } $pagination = min($pagination, $data['pages']['totalPage']); if($data['pages']['currentPage'] - floor($pagination / 2) <= 0) { $frames['left'] = 1; $frames['right'] = $frames['left'] + $pagination - 1; } else if($data['pages']['currentPage'] + floor($pagination / 2) >= $data['pages']['totalPage']) { $frames['right'] = $data['pages']['totalPage']; $frames['left'] = $frames['right'] - $pagination + 1; } else { $frames['left'] = $data['pages']['currentPage'] - floor($pagination / 2); $frames['right'] = $data['pages']['currentPage'] + ceil($pagination / 2) - 1; } $data['start'] = $frames['left']; $data['pages']['start'] = $data['start']; $data['pages']['end'] = $frames['right']; $data['pages']['pagination'] = $pagination; $data['pages']['limitStart'] = (min($page, $data['pages']['totalPage']) - 1) * $pageSize; $data['pages']['pageSize'] = (int) $pageSize; return true; } /** * response as object * * @return mixed|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]] ); } /** * 方法名 + 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'] : ($policy['life_time'] ?? $this->defaultExpired); $res = call_user_func_array(array($this, $method), $args); $str = serialize($res); $func->set($key, $str, ['life_time' => $expiredAt]); return $res; } } return call_user_func_array(array($this->db(), $method), $args); } /** * 开始事务 * * @return mixed */ public function startTrans() { return $this->db()->transaction(); } /** * 提交事务 * * @return mixed */ public function commit() { return $this->db()->commit(); } /** * 回退 * * @return mixed */ public function rollback() { return $this->db()->rollback(); } /** * 验证规则,由子类继承去修改 * * @return array */ public function rules() { return []; } }