<?php
namespace Qii\Driver\Entity;
use Qii\Driver\Model;
use Qii\Driver\Response;
use Qii\Exceptions\InvalidParams;

class Base {
    /**
     * 字段及值
     *
     * @var array $properties
     */
    protected $properties;
    /**
     * 配置信息
     *
     * @var array $config
     */
    public static $config;

    /**
     * @var Model $db
     */
    public static $db = null;
    /**
     * 主键
     * @var mix $privateKeys null
     */
    protected $privateKeys = null;
    /**
     * 唯一字段
     *
     * @var array $uniqKeys
     */
    protected $uniqKeys = null;
    /**
     * 设置排除项
     *
     * @var array $exclude
     */
    protected $exclude = [];
    /**
     * @var string 表别名
     */
    private $alias;

    private $nullFields = [];
    /**
     * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存 / with 关联查询
     * @var array $hooker
     */
    private $hooker = array();
    /**
     * 默认过期时间
     *
     * @var int $defaultExpired
     */
    private $defaultExpired = 600;

    /**
     * 当前执行的sql
     *
     * @var string
     */
    private $calledSQL = "";
    /**
     * 最后插入数据的自增ID
     *
     * @var int $lastInsertID
     */
    private $lastInsertID;

    /** 表名
     *
     * @var null
     */
    private $tableName = null;

    /**
     * 错误信息
     *
     * @var array $__Error {code:0, msg:'', body:mixed}
     */
    public $__Error;

    public function __construct(){
        $this->cleanVars();
    }

    /**
     * 初始化的时候清除所有静态变量的值
     *
     * @return void
     */
    final public function cleanVars() {
        $this->properties = [];
        $this->uniqKeys = null;
        $this->privateKeys = null;
        $this->exclude = [];
    }
    /**
     * 默认值，仅在新增数据的时候支持，更新不支持
     *
     * @return array
     */
    public function defaultFieldsValue(){}

    /**
     * 设置表名
     *
     * @param $tableName
     * @return void
     */
    public function setTableName($tableName) {
        if(is_callable($tableName)) {
            $this->tableName = $tableName();
        }
        if($tableName) $this->tableName = $tableName;
    }
    /**
     * 返回 table 的名字，以便分库分表的时候指定使用
     *
     * @return mixed
     * @throws \Exception
     */
    public function getTable() {
        if($this->tableName != null) {
            return $this->tableName;
        }
        throw new \Exception('请先设置需要保存的数据表');
    }

    /**
     * 当前表名别名
     *
     * @param string $name
     * @return $this
     */
    public function alias($name) {
        $this->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 查询
     * @param array | string $condition 条件
     * @param mixed $args 参数
     * @return $this
     * @throws \Exception
     */
    final public function or($condition, $args = null) {
        $this->setOrHooker(function($or) use($condition){
            if(is_array($condition)) {
                $or = array_merge($or, $condition);
                return $or;
            }
            return $condition;
        });
        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 条件 多个where就用and来连接
     * @param array $where where条件
     * @param mixed $args 参数
     * @return $this
     * @throws \Exception
     */
    final public function where($where, $args = null) {
        $this->setWhereHooker(function($w) use($where){
            /*if(is_array($where)) {
                $w[] = $where;
                return $w;
            }*/
            $w[] = $where;
            return $w;
        }, $args);
        return $this;
    }

    /**
     * 设置where hooker
     * @param object $hooker
     * @return $this
     * @throws \Exception
     */
    public function setWhereHooker($hooker, $args = null) {
        $this->checkCallable($hooker);

        if(!isset($this->hooker['where'])) {
            $this->hooker['where'] = [];
        }
        $this->hooker['where'][] = ['func' => $hooker, 'args' => $args];
        return $this;
    }
    /**
     * 获取where条件
     *
     * @return array|mixed
     */
    public function getWhereHooker() {
        $where = $this->properties();
        if(!isset($this->hooker['where']) || !is_array($this->hooker['where'])) {
            return $where;
        }
        // 遍历
        foreach ($this->hooker['where'] as $item) {
            if(is_callable($item['func'])) {
                $subWhere = call_user_func($item['func'], $where, $item['args']);
                foreach ($subWhere as $key => $value) {
                    if($this->alias != "") {
                        if(!preg_match("/{$this->alias}\.{$key}/", $key)) {
                            $where[$this->alias . '.' . $key] = $value;
                            unset($subWhere[$key]);
                        }
                    }else{
                        $where[$key] = $value;
                    }
                }
            }
        }
        return $where;
    }

    /**
     * join 条件
     * @param mixed $join 条件
     * @param mixed $args 参数
     * @return $this
     * @throws \Exception
     */
    final public function join($join, $args = null) {
        $this->setJoinHooker(function($j) use($join){
            $j[] = $join;
            return $j;
        }, $args);
        return $this;
    }
    /**
     * 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;
    }

    /**
     * 设置group
     *
     * @param array $group group对应的数组
     * @param mixed $args
     * @return void
     */
    final public function group($group, $args = null) {
        if(!isset($this->hooker['group'])) {
            $this->hooker['group'] = [];
        }
        $this->hooker['group'][] = ['func' => function ()use ($group) {
            return $group;
        }, 'args' => $args];

        return $this;
    }

    /**
     * get group hooker
     * @return array
     */
    final public function getGroupBy() {
        if(!isset($this->hooker['group']) || !is_array($this->hooker['group'])) {
            return [];
        }
        $group = [];
        foreach ($this->hooker['group'] as $value) {
            $group = array_merge(call_user_func($value['func'], [], $value['args']));
        }
        return $group;
    }
    /**
     * order by
     * @param array $order
     * @param mix $args 参数
     * @return $this
     * @throws \Exception
     */
    final public function order($order, $args = null) {
        $this->setOrderHooker(function() use($order) {
            return $order;
        }, $args);
        return $this;
    }
    /**
     * 设置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
     * @param  array $limit [page, limit]
     * @param mixed $args 参数
     * @return $this
     * @throws \Exception
     */
    final public function limit($limit, $args = null) {
        $this->setLimitHooker(function() use($limit){
            return $limit;
        }, $args);
        return $this;
    }
    /**
     * 设置 limit hooker
     *
     * @param object $hooker
     * @return $this
     * @throws \Exception
     */
    public function setLimitHooker($hooker, $args = null) {
        $this->checkCallable($hooker);
        $this->setHooker('limit', $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 [];
    }

    /**
     * 设置缓存插件
     *
     * @param array $cache 缓存类
     * @return $this
     * @throws \Exception
     */
    final public function cache($cache) {
        $this->setCacheHooker(function() use($cache){
            return $cache;
        });
        return $this;
    }
    /**
     * 设置 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();
    }

    /**
     * with
     *
     * @param array $with 获取信息 [$rel, 'with name', 'hasOne/hasMany', 'key', 'rel_pri_key']
     * @return $this
     * @throws \Exception
     */
    public function with($with) {
        $this->setWith(function ()use($with){
            return $with;
        });
        return $this;
    }
    /**
     * 设置关联子查询
     *
     * @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() {
        if(self::$db !== null && self::$db instanceof Model) {
            return self::$db;
        }
        return _loadClass('\Qii\Driver\Model');
    }

    /**
     * DB 必须是Model类型
     *
     * @param $db
     * @return void
     * @throws \Exception
     */
    final public function setDB($db) {
        if(!($db instanceof \Qii\Driver\Model)) {
            throw new \Exception('DB is illegal');
        }
        self::$db = $db;
    }
    /**
     * 返回 entity 信息
     * @return \Qii\Driver\Entity\Entity
     */
    public function entity() {
        return _loadClass('\Qii\Driver\Entity\Entity');
    }
    /**
     * 设置查询字段
     *
     * @param array|string $fields 字段名称
     * @param $args
     * @return void
     * @throws \Exception
     */
    final public function field($fields, $args = null) {
        $this->setQueryFieldsHooker(function() use($fields){
            return $fields;
        }, $args);
    }

    /**
     * 返回所有 Fields
     * @return array
     */
    public function fields()
    {
        return [];
    }

    /**
     * 设置唯一字段
     *
     * @param mixed $uniqKeys 唯一字段
     * @return void
     */
    public function setUniqKeys($uniqKeys) {
        $this->uniqKeys = $uniqKeys;
    }
    /**
     * unique (unique 如果是 array('a,b') 则表示 a,b联合唯一，array('a,b', 'b,c') 表示a,b唯一 或 b,c唯一
     * 'a,b' 字符串则表示任意唯一 类似于array ('a','b')
     *
     * @return mixed
     * @throws \Exception
     */
    public function uniqueKey(){
        if($this->uniqKeys !== null) {
            return $this->uniqKeys;
        }
        throw new \Exception('请设置唯一值');
    }

    /**
     * 设置主键
     *
     * @param mixed $privateKeys
     * @return void
     */
    public function setPrivateKey($privateKeys){
        $this->privateKeys = $privateKeys;
    }
    /**
     * 主键
     *
     * @return mixed
     * @throws \Exception
     */
    public function primaryKey(){
        if($this->privateKeys !== null) {
            return $this->privateKeys;
        }
        throw new \Exception('请设置主键');
    }

    /**
     * 设置排除项
     * @param array $exclude
     * @return void
     */
    public function setExclude($exclude) {
        if(!is_array($exclude)) {
            return;
        }
        $this->exclude = $exclude;
    }
    /**
     * 保存数据的时候，验证唯一需要排除的值，此处仅支持，单个或联合排除，不支持单个异或排除
     *
     * @return array
     */
    public function exclude(){
        if($this->exclude !== null) {
            return $this->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的属性');
    }


    /**
     * 更新数据验证字段
     *
     * $fields = [];
     * return $this->valid($fields);
     *
     * @return mixed
     * @throws \Exception
     */
    public function validFieldsForReplace() {
        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) || !preg_match('/^[A-Z\_].*?/', $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');
        $query->groupBy($this->getGroupBy());
        return $query->selectOne($this->prepareTable());
    }
    /**
     * 检查是否有对应的属性
     *
     * @param $key
     * @return bool
     * @throws \ReflectionException
     */
    private $__property = [];
    public function hasProperty($key) {
        $class = get_called_class();
        $key = $this->entity()->convertToProperty($key);
        if(isset($this->properties[$class])) {
            return isset($this->properties[$class][$key]);
        }
        $method = new \ReflectionClass($class);
        $properties = $method->getproperties();
        $fields = array();
        foreach($properties as $property) {
            $name = $property->getName();
            if($property->isPublic() && preg_match('/^[A-Z\_].*?/', $name)) {
                $fields[$name] = '';
            }
        }
        $this->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)) {
                if(is_array($value)) {
                    $value = json_encode($value, JSON_UNESCAPED_UNICODE);
                }
                if(is_object($value)) {
                    $value = serialize($value);
                }
                $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;
    }

    /**
     * 获取错误信息
     * @return array
     */
    public function getErrorInfo() {
        if(!$this->isError()) {
            return ['code' => 0, 'msg' => ''];
        }
        return ['code' => $this->getCode(), 'msg' => $this->getMessage()];
    }
    /**
     * 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->executeSQL;
            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
     */
    final 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;
    }

    /**
     * 将结果转换成json
     *
     * @return false|string
     */
    final public function toJson() {
        $arr = $this->toArray();
        return json_encode($arr, JSON_UNESCAPED_UNICODE);
    }
    /**
     * 是否相关数据存在
     *
     * @return bool
     */
    final public function exist() {
        $where = $this->getWhereHooker();
        if(!$where) {
            return false;
        }
        return (bool) $this->db()->where($where)->selectOne($this->prepareTable());
    }

    /**
     * replace into database
     *
     * @return mixed|Response
     * @throws InvalidParams
     */
    final public function replace(){
        $valid = $this->validFieldsForReplace();
        if($valid->isError()) {
            return $valid;
        }
        //如果设置了 unique 就先验证唯一性
        list($uniqueWhere, $uniqueOr, $exclude, $primary) = $this->condition();
        unset($uniqueWhere, $uniqueOr, $exclude, $exclude);
        if(count($primary) == 0) {
            throw new InvalidParams('DB::replace需要设置主键');
        }
        $values = array_merge($this->getDefaultValue(), $this->properties());
        $this->lastInsertID = $res = $this->db()->replaceObject($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 mixed|Response
     * @throws \Exception
     */
    final 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' => '数据已经存在(1)', 'body' => $exist],
                        'message' => '数据已经存在(1)'
                    ]
                );
            }
        }

        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' => '数据已经存在(2)', 'body' => $exist],
                        'message' => '数据已存在(2)'
                    ]
                );
            }
        }

        $values = array_merge($this->getDefaultValue(), $this->properties());
        $this->lastInsertID = $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() . "(3)", 'body' => []],
                    'message' => $this->db()->getMessage() . "(3)"
                ]);
        }
        return Response::Success(static::class .'::'. __FUNCTION__,
            [
                '_result' => ['code' => Response::DO_SUCCESS, 'msg' => '添加成功', 'body' => $res]
            ]
        );
    }

    /**
     * 获取最后一条插入记录的自增ID
     *
     * @return mixed
     */
    final public function lastInsertID() {
        return $this->lastInsertID;
    }
    /**
     * 删除指定数据
     *
     * @return false|Response
     */
    final public function remove() {
        if(!$this->properties() && empty($this->getWhereHooker())) {
            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 . '条记录'
            ]
        );
    }

    /**
     * 排除操作符
     *
     * @param array $array1 数组1
     * @param array $array2 数组2
     * @return array
     */
    final public function array_diff_assoc($array1, $array2) {
        $arr = array_diff_assoc($array1, $array2);
        foreach($arr as $k => $v) {
            if(is_numeric($k) && in_array($v, ['or', 'and'])) {
                unset($arr[$k]);
            }
        }
        return $arr;
    }
    /**
     * 更新
     *
     * @return mixed | Response
     * @throws \Exception
     */
    final public function update(){
        $valid = $this->validFieldsForUpdate();
        if($valid->isError()) {
            return $valid;
        }
        //检查是否有重复的
        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
        // 检查 unique 是否已经存在相关数据
        $diffWhere = $this->array_diff_assoc($uniqueWhere, $primaryKey);
        $diffOr = $this->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' => []]
                ]
            );
        }
        $msg = '更新成功';
        if($affectedRows > 0) {
            $msg .= '，共更新了'. $affectedRows . '条记录';
        }
        return Response::Success(static::class .'::'. __FUNCTION__,
            [
                '_result' => ['code' => Response::DO_SUCCESS, 'msg' => $msg, 'body' => $affectedRows],
                'message' => $msg
            ]
        );
    }

    /**
     * 更新字段
     *
     * @return mixed| Response
     * @throws \Exception
     */
    final 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 = $this->array_diff_assoc($uniqueWhere, $primaryKey);
        $diffOr = $this->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
     */
    final public function incr() {
        list($where, $or, $exclude, $primary) = $this->condition();
        unset($where, $or, $exclude);
        $property = $this->properties();
        $incr = [];
        $sets = [];
        foreach ($property as $key => $value) {
            if(!is_numeric($value)) {
                $sets[$key] = $value;
                continue;
            }
            $incr[$key] = $value;
        }
        $diff = $this->array_diff_assoc($incr, $primary);
        if(count($diff) == 0) {
            return Response::Fail(self::class .'::'. __FUNCTION__,
                [
                    '_result' => ['code' => Response::DO_FAIL, 'msg' => 'INCR 参数错误'],
                    'message' => 'INCR 参数错误',
                ]
            );
        }
        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[]
     */
    final public function getFields() {
        $fields = $this->getFieldsHooker();
        if(!is_array($fields)) {
            $fields = explode(',', preg_replace("/\s{2,}/", " ", preg_replace("/\s(~as)/", "", $fields)));
        }
        return $fields;
    }
    /**
     * 获取where及or条件并创建查询条件
     *
     * @return \Qii\Driver\Base
     */
    final 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
     */
    final public function first() {
        $orderBy = $this->getOrderBy();
        foreach ($orderBy as $key => $value) {
            $orderBy[$key] = 'ASC';
        }
        $query = $this->createQuery();
        $row = $query->limit(1)->groupBy($this->getGroupBy())->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
     */
    final public function last() {
        $orderBy = $this->getOrderBy();
        foreach ($orderBy as $key => $value) {
            $orderBy[$key] = 'DESC';
        }
        $query = $this->createQuery();
        $row = $query->groupBy($this->getGroupBy())->orderBy($orderBy)->limit(1)->selectRow($this->prepareTable());
        $this->calledSQL = $query->executeSQL;
        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
     */
    final 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
     */
    final public function rs($page = null, $pageSize = null) {
        $query = $this->createQuery()->groupBy($this->getGroupBy())->orderBy($this->getOrderBy());
        if($page && $pageSize) {
            $start = ((max($page, 1)) - 1) * $pageSize;
            $query->limit($start, $pageSize);
        }
        return $query->rs($this->prepareTable());
    }

    /**
     * select rows
     * @param int $page 页码
     * @param int $pageSize 页大小
     * @return array
     * @throws \Qii\Exceptions\InvalidParams
     */
    final public function select($page = null, $pageSize = null) {
        $limit = $this->getLimitHooker();
        if(count($limit) > 0) {
            $page = $limit[0];
            $pageSize = isset($limit[1]) ? $limit[1] : null;
        }
        $query = $this->createQuery();
        $query = $query->groupBy($this->getGroupBy());
        $query = $query->orderBy($this->getOrderBy());
        if($page !== null) {
            $query = $query->limit($page, $pageSize);
        }
        $lists = $query->selectRows($this->prepareTable());
        $this->withList($lists);
        return $lists;
    }
    /**
     * listts
     * @param int $page 页码
     * @param int $pageSize 页大小
     * @return array
     * @throws \Exception
     */
    final public function lists($page = 1, $pageSize = 20) {
        $limit = $this->getLimitHooker();
        if(count($limit) > 0) {
            $page = $limit[0];
            $pageSize = isset($limit[1]) ? $limit[1] : null;
        }
        $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;*/
        $data['lists'] = $this->select($data['pages']['limitStart'], $pageSize);
        return $data;
    }
    /**
     * 附带一行数据
     *
     * @param array $lists
     * @return void
     */
    final 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];
                }
            }
            if(count($rel) == 0) {
                return;
            }
            // [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
     */
    final 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
     */
    final 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
     */
    final public function listAll() {
        $limit = $this->getLimitHooker();
        $query = $this->createQuery()->groupBy($this->getGroupBy())->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
     */
    final 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
     */
    final 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() {
        $unique = $this->uniqueKey();
        if(!is_array($unique)) {
            if(strpos($unique, ',') !== false) {
                $unique = explode(',', $unique);
            }else{
                $unique = array($unique);
            }
        }
        $where = ['and' => [], 'or' => []];
        $a1 = array_reduce($unique, function ($where, $item){
            if(strpos($item, ',') !== false) {
                $item = explode(',', $item);
                foreach($item as $k){
                    $key = $this->entity()->convertToField($k);
                    $property = $this->entity()->convertToProperty($k);
                    $where['and'][$key] = $this->$property;
                }
            }else{
                $key = $this->entity()->convertToField($item);
                $property = $this->entity()->convertToProperty($item);
                $where['or'][$key] = $this->$property;
                $where['or'][] = 'or';
            }
            return $where;
        }, $where);
        //弹出or的最后一个or操作符
        array_pop($a1['or']);
        return [$a1['and'], $a1['or']];
    }

    /**
     * exclude Condition
     *
     * @return array
     */
    protected function excludeCondition() {
        $exclude = $this->exclude();
        if(!$exclude) {
            $exclude = $this->primaryKey();
        }
        if(!is_array($exclude)) {
            $exclude = (array) $this->explode(',', $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 = intval(max(1, $page));
        $pageSize = intval(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'] = isset($data['lists']) ? $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
     */
    final 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)) {
                if($cacheID != '') {
                    $key = $cacheID;
                }else{
                    $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
     */
    final public function startTrans() {
        return $this->db()->transaction();
    }

    /**
     * 提交事务
     *
     * @return mixed
     */
    final public function commit() {
        return $this->db()->commit();
    }

    /**
     * 回退
     *
     * @return mixed
     */
    final public function rollback() {
        return $this->db()->rollback();
    }
    /**
     * 验证规则，由子类继承去修改
     *
     * @return array
     */
    public function rules()
    {
        return [];
    }
}