<?php
namespace Qii\Driver\Entity;

use \Qii\Driver\Response;

class Base {
    /**
     * 字段及值
     *
     * @var array $properties
     */
    public static $properties;
    public static $config;
    /**
     * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存
     * @var array $hooker
     */
    private $hooker = [];
    /**
     * 默认过期时间
     *
     * @var int $defaultExpired
     */
    private $defaultExpired = 600;

    public function __construct(){
    }

    /**
     * 默认值，仅在新增数据的时候支持，更新不支持
     *
     * @return array
     */
    public function defaultFieldsValue(){}

    /**
     * 返回 table 的名字，以便分库分表的时候指定使用
     *
     * @return mixed
     * @throws \Exception
     */
    public function getTable() {
        throw new \Exception('请先设置需要保存的数据表');
    }
    /**
     * set hooker for method
     * 目前 where 和 order 及 fields 支持
     * @param $key
     * @param $hooker
     * @return void
     */
    protected function setHooker($key, $hooker, $args) {
        $this->hooker[$key]['func'] = $hooker;
        $this->hooker[$key]['args'] = $args;
        return $this;
    }

    /**
     * 设置where hooker
     * @param object $hooker
     * @return void
     * @throws \Exception
     */
    final 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']) && $this->hooker['where'] && is_callable($this->hooker['where']['func'])) {
            $where = call_user_func($this->hooker['where']['func'], $this->properties(), $this->hooker['where']['args']);
        }
        return $where;
    }

    /**
     * 设置order by hooker
     * @param object $hooker
     * @return void
     * @throws \Exception
     */
    final public function setOrderHooker($hooker, $args = null) {
        $this->checkCallable($hooker);
        $this->setHooker('order', $hooker, $args);
        return $this;
    }

    /**
     * 设置 limit hooker
     *
     * @param object $hooker
     * @return void
     * @throws \Exception
     */
    final public function setLimitHooker($hooker, $args = null) {
        $this->checkCallable($hooker);
        $this->setHooker('limit', $hooker, $args);
        return $this;
    }
    /**
     * 获取limit
     * @return int|mixed
     */
    final public function getLimitHooker() {
        if(isset($this->hooker['limit']) && $this->hooker['limit'] && is_callable($this->hooker['limit']['func'])) {
            return call_user_func($this->hooker['limit']['func'], [], $this->hooker['limit']['args']);
        }
        return [];
    }

    /**
     * 设置 cache hooker
     *
     * @param callable $hooker
     * @param array| mixed $args
     * @return void
     * @throws \Exception
     */
    final public function setCacheHooker($hooker, $args = null) {
        $this->checkCallable($hooker);
        $this->setHooker('cache', $hooker, $args);
        return $this;
    }

    /**
     * 获取Cache Hooker
     * @return mixed|null[]
     */
    final public function getCacheHooker() {
        if(isset($this->hooker['cache']) && $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
     */
    final public function setCacheConfig($config) {
        if(!is_array($config)) {
            return;
        }
        self::$config['cache'] = $config;
    }

    /**
     * 获取缓存的配置信息
     *
     * @return array
     */
    final public function getCacheConfig() {
        if(is_array(self::$config) && isset(self::$config['cache']) && is_array(self::$config['cache'])) {
            return self::$config['cache'];
        }
        return array();
    }
    /**
     * 如果hooker不可调用即抛出异常
     *
     * @param object $hooker
     * @return void
     * @throws \Exception
     */
    private function checkCallable($hooker) {
        if($hooker && !is_callable($hooker)) {
            throw new \Exception('Hooker is uncallable');
        }
    }
    /**
     * 设置order by hooker
     * @param object $hooker
     * @return void
     * @throws \Exception
     */
    final public function setQueryFieldsHooker($hooker, $args = null) {
        if(!is_callable($hooker)) {
            throw new \Exception('Hooker is un callable');
        }
        $this->setHooker('fields', $hooker, $args);
        return $this;
    }

    /**
     * 通过field hooker返回查询的字段
     *
     * @return mixed|string
     */
    final public function getFieldsHooker() {
        if(isset($this->hooker['fields']) && $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
     */
    final 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 mixed
     */
    final public function db() {
        return _loadClass('\Qii\Driver\Model');
    }

    /**
     * 返回 entity 信息
     * @return mixed
     */
    final public function entity() {
        return _loadClass('\Qii\Driver\Entity\Entity');
    }

    /**
     * unique (unique 如果是 array 则表示 联合唯一，如果是string则是任意唯一，多个单独唯一使用字符串以逗号隔开, 主键除外)
     *
     * @return mixed
     * @throws \Exception
     */
    public function uniqueKey(){
        throw new \Exception('请设置唯一值');
    }

    /**
     * 主键
     *
     * @return mixed
     * @throws \Exception
     */
    public function primaryKey(){
        throw new \Exception('请设置主键');
    }

    /**
     * 保存数据的时候，验证唯一需要排除的值，此处仅支持，单个或联合排除，不支持单个排除
     *
     * @return array
     */
    public function 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的属性');
    }
    /**
     * 获取不为空的属性
     *
     * @return array
     */
    final public function properties() {
        $class = get_called_class();
        $method = new \ReflectionClass($class);
        $properties = $method->getproperties();
        $fields = [];
        foreach($properties as $property) {
            if($property->isPublic()) {
                $name = $property->getName();
                if(!isset($this->$name)) {
                    continue;
                }
                $field = $this->entity()->convertToField($name);
                $fields[$field] = $this->$name;
            }
        }
        return $fields;
    }

    /**
     * 获取默认值
     *
     * @return array
     */
    final 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
     */
    final public function count() {
        return $this->db()->fields(' COUNT(1) as count')->where($this->getWhereHooker())->selectOne($this->getTable());
    }

    /**
     * 检查是否有对应的属性
     *
     * @param $key
     * @return bool
     */
    final 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|false
     */
    final 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;
    }
    /**
     * 获取数据
     *
     * @return Object
     * @throws \Exception
     */
    final public function get() {
        $class = get_called_class();
        $obj = new $class();
        $response = $this->info();
        if($response->isError()) {
            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不同的这个返回的是Response
     * @return mixed|\Qii\Driver\Qii\Driver\Response
     */
    final public function info() {
        try{
            $info = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->selectRow($this->getTable());
            return Response::Success(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $info]]
            );
        }catch (\Exception $e) {
            return Response::Fail(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => $this->db()->getMessage(), 'body' => []]
                ]);
        }
    }
    /**
     * 是否相关数据存在
     *
     * @return bool
     */
    final public function exist() {
        $where = $this->getWhereHooker();
        if(!$where) {
            return false;
        }
        return (bool) $this->db()->where($where)->selectOne($this->getTable());
    }
    /**
     * 保存数据
     *
     * @return mixed|Response
     * @throws \Exception
     */
    final public function add() {
        $valid = $this->validFieldsForAdd();
        if($valid->isError()) {
            return $valid;
        }
        //如果设置了 unique 就先验证唯一性
        list($uniqueWhere, $exclude, $primary) = $this->condition();
        unset($exclude);

        //如果 $where $or 为空，看看主键是否有设置值，有设置值说明验证主键就可以，反之设置了主键，主键值设置了，只验证主键就可以了
        if(count($primary) > 0) {
            $exist = $this->db()->limit(1)->where($primary)->selectRow($this->getTable());
            if($exist) {
                return Response::Exist(static::class .'::'. __FUNCTION__,
                    ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist]]
                );
            }
        }

        if(count($uniqueWhere) > 0) {
            $exist = $this->db()->limit(1)->where($uniqueWhere)->selectRow($this->getTable());
            if($exist) {
                return Response::Exist(static::class .'::'. __FUNCTION__,
                    ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist]]
                );
            }
        }

        $values = array_merge($this->getDefaultValue(), $this->properties());
        $res = $this->db()->insertObject($this->getTable(), $values);

        if($this->db()->isError()) {
            return Response::FailSave(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => []]
                ]);
        }
        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::FailRemove(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => '未指定删除属性，不允许删除', 'body' => 0]
                ]);
        }
        $properties = [];
        foreach ($this->properties() as $key => $val) {
            $properties[] = $this->entity()->convertToProperty($key);
        }

        $valid = $this->valid($properties);
        if($valid->isError()) {
            return $valid;
        }
        $affectedRows = $this->db()->where($this->getWhereHooker())->delete($this->getTable());
        if($this->db()->isError()) {
            return Response::FailRemove(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => 0]
                ]);
        }
        return Response::Success(static::class .'::'. __FUNCTION__,
            ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '删除成功,总共删除了'. $affectedRows . '条记录', 'body' => $affectedRows]]
        );
    }
    /**
     * 更新
     *
     * @return mixed | Response
     * @throws \Exception
     */
    final public function update(){
        $valid = $this->validFieldsForUpdate();
        if($valid->isError()) {
            return $valid;
        }
        //检查是否有重复的
        list($uniqueWhere, $exclude, $primaryKey) = $this->condition();
        // 检查 unique 是否已经存在相关数据
        $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
        if(count($diffWhere) > 0) {
            $unique = $this->db()->limit(1)->where($diffWhere)->exclude($exclude)->selectRow($this->getTable());
            if($unique) {
                return Response::Exist(static::class .'::'. __FUNCTION__,
                    ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]]
                );
            }
        }
        $where = [];
        $whereHooker = $this->getWhereHooker();
        foreach ($whereHooker as $key => $val) {
            $keyArr = explode(':', $key);
            if(isset($primaryKey[$keyArr[0]])) {
                unset($primaryKey[$keyArr[0]]);
                $where[$key] = $val;
            }
        }
        if(count($where) > 0) {
            $primaryKey = $where;
        }
        //检查更新的数据是否存在，以主键为主
        $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->getTable());
        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->getTable(), $this->properties(), $primaryKey);
        if($this->db()->isError()) {
            return Response::FailUpdate(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
            );
        }
        return Response::Success(static::class .'::'. __FUNCTION__,
            ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '数据更新成功', 'body' => $affectedRows]]
        );
    }

    /**
     * 更新字段
     *
     * @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, $exclude, $primaryKey) = $this->condition();
        // 检查 unique 是否已经存在相关数据
        $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);

        if(count($diffWhere) > 0) {
            $unique = $this->db()->limit(1)->where($diffWhere)->exclude($exclude)->selectRow($this->getTable());
            if($unique) {
                return Response::Exist(static::class .'::'. __FUNCTION__,
                    ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]]
                );
            }
        }

        /*$whereHooker = $this->getWhereHooker();
        if(!empty($whereHooker)) {
            $primaryKey = $whereHooker;
        }*/
        $where = [];
        $whereHooker = $this->getWhereHooker();
        foreach ($whereHooker as $key => $val) {
            $keyArr = explode(':', $key);
            if(isset($primaryKey[$keyArr[0]])) {
                unset($primaryKey[$keyArr[0]]);
                $where[$key] = $val;
            }
        }
        if(count($where) > 0) {
            $primaryKey = $where;
        }

        //检查更新的数据是否存在，以主键为主
        $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->getTable());
        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->getTable(), $this->properties(), $primaryKey);
        if($this->db()->isError()) {
            return Response::FailUpdate(static::class .'::'. __FUNCTION__,
                ['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
            );
        }
        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, $exclude, $primary) = $this->condition();
        unset($where, $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 参数错误']]);
        }
        $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;
            }
        }
        $whereHooker = $this->getWhereHooker();
        if(!empty($whereHooker)) {
            foreach ($whereHooker as $key => $where) {
                //去掉where条件里边的sets的值
                $keys = $this->explode(":", $key);
                if(isset($diff[$keys[0]])) {
                    unset($whereHooker[$key]);
                }
            }
            $primary = $whereHooker;
        }
        $affectedRows = $this->db()->set($sets)->where($primary)->update($this->getTable());

        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' => 'INCR 成功', 'body' => $affectedRows]]
        );
    }

    /**
     * 获取第一条数据
     *
     * @return mixed|Response
     * @throws \Exception
     */
    final public function first() {
        $orderBy = $this->getOrderBy();
        //未设置order by 就以主键为准
        if(empty($orderBy)) {
            $orderBy = $this->primaryCondition();
        }
        foreach ($orderBy as $key => $value) {
            $orderBy[$key] = 'ASC';
        }
        $row = $this->db()->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
        if($this->db()->isError()) {
            return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
        }
        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();
        //未设置order by 就以主键为准
        if(empty($orderBy)) {
            $orderBy = $this->primaryCondition();
        }
        foreach ($orderBy as $key => $value) {
            $orderBy[$key] = 'DESC';
        }
        $row = $this->db()->fields($this->getFieldsHooker())->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
        if($this->db()->isError()) {
            return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
        }
        return Response::Success(static::class .'::'. __FUNCTION__,
            ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'Query 成功', 'body' => $row]]
        );
    }
    /**
     * listts
     * @param int $page 页码
     * @param int $pageSize 页大小
     * @return array
     * @throws \Exception
     */
    final public function lists($page = 1, $pageSize = 20) {
        $count = $this->count();
        if(!$this->initPages($data, $count, $page, $pageSize)) {
            return $data;
        }
        $data['lists'] = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
        return $data;
    }

    /**
     * 获取所有数据，这里不分页，但支持限条数，通过 setLimitHooker来实现
     * $this->setLimitHooker(function(){
     * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的
     * });
     * @return mixed
     * @throws \Exception
     */
    final public function listAll() {
        return $this->rsCondition()->selectRows($this->getTable());
    }
    /**
     * 返回指针
     * @return mixed
     * @throws \Exception
     */
    final public function rs() {
        return $this->rsCondition()->rs($this->getTable());
    }

    /**
     * 通过此方法获取 rs
     * @return mixed
     */
    private function rsCondition() {
        $limit = $this->getLimitHooker();
        if(empty($limit) || !is_array($limit)) {
            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy());
        }
        if(count($limit) == 1) {
            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0]);
        }
        return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0], $limit[1]);
    }
    /**
     * 验证相关字段
     *
     * @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' => []]]);
        }
        $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]]);
        }
        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\Drive\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']]]);
        }
        return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]);
    }

    /**
     * unique 条件
     *
     * @return array[]
     * @throws \Exception
     */
    protected function uniqueCondition() {
        $uniqueWhere = [];
        $unique = $this->uniqueKey();
        $useWhere = true;
        $condition = [];
        if(empty($unique)) {
            return $condition;
        }
        if(!is_array($unique)) {
            $unique = $this->explode(',', $unique);
            foreach ($unique as $val) {
                $condition[] = $val;
            }
        }else{
            //检查是否是一维数组，一维数组联合唯一
            if(count($unique) == count($unique, 1)) {
                $condition[] = $unique;
            }else{
                $condition = $unique;
            }

        }
        foreach ($condition as $item) {
            $uniqueWhere[] = "OR";
            $arr = [];
            foreach ($item as $key) {
                $key = $this->entity()->convertToField($key);
                $property = $this->entity()->convertToProperty($key);
                $arr[$key] = $this->$property;
            }
            $uniqueWhere[] = $arr;
        }
        return $uniqueWhere;
    }

    /**
     * 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]
     */
    final public function condition() {
        //如果设置了 unique 就先验证唯一性，保存的时候验证 uniqueKey，更新的时候验证uniqueKey并排primaryKey
        //保存数据的时候验证uniqueKey；更新时验证 uniqueKey，并且排除 primaryKey，如果uniqueKey == primaryKey则不去做唯一性验证
        $uniqueWhere = $this->uniqueCondition();
        $excludeCondition = $this->excludeCondition();
        $primaryCondition = $this->primaryCondition();
        return [$uniqueWhere, $excludeCondition, $primaryCondition];
    }

    /**
     * 获取order by
     *
     * @return array
     */
    final public function getOrderBy() {
        $order = array();
        $orderBy = (array) $this->orderBy();
        if(isset($this->hooker['order']) && is_callable($this->hooker['order']['func'])) {
            $orderBy = (array) call_user_func($this->hooker['order']['func'], $order, $this->hooker['order']['args']);
        }
        foreach ($orderBy as $key => $val) {
            $field = $this->entity()->convertToField($key);
            $val = strtoupper($val);
            if(!in_array($val, array('DESC', 'ASC'))) {
                continue;
            }
            $order[$field] = $val;
        }
        return (array) $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;
        }
        $frames = array(
            'left' => 0,
            'right' => 0
        );
        $pagination = $pagination < $data['pages']['totalPage'] ? $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|\Qii\Driver\Qii\Driver\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)) {
                $key = get_called_class() .':'. $method .':'. substr(md5(serialize($this->properties())), -16);
                if($cache == 'byclean') {
                    return $func->del($key);
                }
                if(is_object($func) && method_exists($func, 'get')) {
                    $res = $func->get($key);
                    if ($res) {
                        return unserialize($res);
                    }
                }
                $expiredAt = isset($config['life_time']) ? (int) $config['life_time'] :
                    (isset($policy['life_time']) ? $policy['life_time'] : $this->defaultExpired);
                $res = call_user_func_array(array($this, $method), $args);
                $str = serialize($res);
                $func->set($key, $str, ['life_time' => $expiredAt]);
                return $res;
            }
        }
        return call_user_func_array(array($this->db(), $method), $args);
    }
}