Explorar el Código

新增 db with

朱金辉 hace 2 meses
padre
commit
3af02c5f90

+ 1 - 1
src/Base/Dispatcher.php

@@ -53,7 +53,7 @@ class Dispatcher
     public function getMiddleWare($controller)
     {
         if (method_exists($controller, 'getMiddleware')) {
-            $this->requestMiddleWare = array_merge($this->requestMiddleWare, $controller->getMiddleware());
+            $this->requestMiddleWare = array_merge($this->requestMiddleWare, (array) $controller->getMiddleware());
         }
         return $this;
     }

+ 1 - 1
src/Driver/Base.php

@@ -432,7 +432,7 @@ class Base
     }
     /**
      * where条件
-     * @param $where where语句
+     * @param array $where where语句
      * @param null $res
      * @return $this
      */

+ 288 - 203
src/Driver/Entity/Base.php

@@ -1,7 +1,9 @@
 <?php
 namespace Qii\Driver\Entity;
 
-use \Qii\Driver\Response;
+
+
+use Qii\Driver\Response;
 
 class Base {
     /**
@@ -12,7 +14,7 @@ class Base {
     public static $properties;
     public static $config;
     /**
-     * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存
+     * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存 / with 关联查询
      * @var array $hooker
      */
     private $hooker = [];
@@ -31,9 +33,7 @@ class Base {
      *
      * @return array
      */
-    public function defaultFieldsValue(){
-        return array();
-    }
+    public function defaultFieldsValue(){}
 
     /**
      * 返回 table 的名字,以便分库分表的时候指定使用
@@ -44,31 +44,27 @@ class Base {
     public function getTable() {
         throw new \Exception('请先设置需要保存的数据表');
     }
-
     /**
      * set hooker for method
      * 目前 where 和 order 及 fields 支持
      * @param $key
      * @param $hooker
-     * @param $args
-     * @return $this
+     * @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 $this
+     * @return void
      * @throws \Exception
      */
-    final public function setWhereHooker($hooker, $args = null) {
+    public function setWhereHooker($hooker, $args = null) {
         $this->checkCallable($hooker);
         $this->setHooker('where', $hooker, $args);
-        return $this;
     }
     /**
      * 获取where条件
@@ -77,7 +73,7 @@ class Base {
      */
     public function getWhereHooker() {
         $where = $this->properties();
-        if(isset($this->hooker['where']) && $this->hooker['where'] && is_callable($this->hooker['where']['func'])) {
+        if(isset($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;
@@ -86,33 +82,31 @@ class Base {
     /**
      * 设置order by hooker
      * @param object $hooker
-     * @return $this
+     * @return void
      * @throws \Exception
      */
-    final public function setOrderHooker($hooker, $args = null) {
+    public function setOrderHooker($hooker, $args = null) {
         $this->checkCallable($hooker);
         $this->setHooker('order', $hooker, $args);
-        return $this;
     }
 
     /**
      * 设置 limit hooker
      *
      * @param object $hooker
-     * @return $this
+     * @return void
      * @throws \Exception
      */
-    final public function setLimitHooker($hooker, $args = null) {
+    public function setLimitHooker($hooker, $args = null) {
         $this->checkCallable($hooker);
-        $this->setHooker('limit', $hooker, $args);
-        return $this;
+        $this->setHooker('order', $hooker, $args);
     }
     /**
      * 获取limit
      * @return int|mixed
      */
-    final public function getLimitHooker() {
-        if(isset($this->hooker['limit']) && $this->hooker['limit'] && is_callable($this->hooker['limit']['func'])) {
+    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 [];
@@ -123,21 +117,20 @@ class Base {
      *
      * @param callable $hooker
      * @param array| mixed $args
-     * @return $this
+     * @return void
      * @throws \Exception
      */
-    final public function setCacheHooker($hooker, $args = null) {
+    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'])) {
+    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];
@@ -149,7 +142,7 @@ class Base {
      * @param array $config 缓存配置
      * @return void
      */
-    final public function setCacheConfig($config) {
+    public function setCacheConfig($config) {
         if(!is_array($config)) {
             return;
         }
@@ -161,12 +154,46 @@ class Base {
      *
      * @return array
      */
-    final public function getCacheConfig() {
+    public function getCacheConfig() {
         if(is_array(self::$config) && isset(self::$config['cache']) && is_array(self::$config['cache'])) {
             return self::$config['cache'];
         }
         return array();
     }
+
+    /**
+     * 设置关联子查询
+     *
+     * @param $hooker
+     * @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 mixed|null[]
+     */
+    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不可调用即抛出异常
      *
@@ -182,15 +209,14 @@ class Base {
     /**
      * 设置order by hooker
      * @param object $hooker
-     * @return $this
+     * @return void
      * @throws \Exception
      */
-    final public function setQueryFieldsHooker($hooker, $args = null) {
+    public function setQueryFieldsHooker($hooker, $args = null) {
         if(!is_callable($hooker)) {
             throw new \Exception('Hooker is un callable');
         }
         $this->setHooker('fields', $hooker, $args);
-        return $this;
     }
 
     /**
@@ -198,8 +224,8 @@ class Base {
      *
      * @return mixed|string
      */
-    final public function getFieldsHooker() {
-        if(isset($this->hooker['fields']) && $this->hooker['fields'] && is_callable($this->hooker['fields']['func'])) {
+    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 '*';
@@ -210,7 +236,7 @@ class Base {
      * @param string $field
      * @return mixed
      */
-    final public function getFieldVal($field) {
+    public function getFieldVal($field) {
         if(!preg_match('/^[a-zA-Z]/', $field)) {
             throw new \Exception('Field is illegal');
         }
@@ -222,7 +248,7 @@ class Base {
      *
      * @return \Qii\Driver\Base
      */
-    final public function db() {
+    public function db() {
         return _loadClass('\Qii\Driver\Model');
     }
 
@@ -230,7 +256,7 @@ class Base {
      * 返回 entity 信息
      * @return mixed
      */
-    final public function entity() {
+    public function entity() {
         return _loadClass('\Qii\Driver\Entity\Entity');
     }
 
@@ -316,7 +342,7 @@ class Base {
      *
      * @return array
      */
-    final public function properties() {
+    public function properties() {
         $class = get_called_class();
         $method = new \ReflectionClass($class);
         $properties = $method->getproperties();
@@ -335,32 +361,11 @@ class Base {
     }
 
     /**
-     * 清除变量内容
-     * @return $this
-     */
-    final public function clear() {
-        $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;
-                }
-                $this->$name = null;
-            }
-        }
-        return $this;
-    }
-
-    /**
      * 获取默认值
      *
      * @return array
      */
-    final public function getDefaultValue() {
+    public function getDefaultValue() {
         $default = [];
         $defaultValue = $this->defaultFieldsValue();
         if(!$defaultValue || !is_array($defaultValue) || count($defaultValue) == 0) {
@@ -379,7 +384,7 @@ class Base {
      * @return mixed
      * @throws \Exception
      */
-    final public function count() {
+    public function count() {
         return $this->db()->fields(' COUNT(1) as count')->where($this->getWhereHooker())->selectOne($this->getTable());
     }
 
@@ -388,8 +393,9 @@ class Base {
      *
      * @param $key
      * @return bool
+     * @throws \ReflectionException
      */
-    final public function hasProperty($key) {
+    public function hasProperty($key) {
         $class = get_called_class();
         $key = $this->entity()->convertToProperty($key);
         if(isset(self::$properties[$class])) {
@@ -412,8 +418,9 @@ class Base {
      * bind value
      * @param $values
      * @return $this
+     * @throws \ReflectionException
      */
-    final public function bindValues($values) {
+    public function bindValues($values) {
         if(!is_array($values)) {
             return $this;
         }
@@ -431,7 +438,7 @@ class Base {
      * @return Object
      * @throws \Exception
      */
-    final public function get() {
+    public function get() {
         $class = get_called_class();
         $obj = new $class();
         $response = $this->info();
@@ -453,11 +460,14 @@ class Base {
      * 与get不同的这个返回的是Response
      * @return mixed|\Qii\Driver\Qii\Driver\Response
      */
-    final public function info() {
+    public function info() {
         try{
             $info = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->selectRow($this->getTable());
-            return Response::Success(
-                static::class .'::'. __FUNCTION__,
+
+            if($info) {
+                $this->withRow($info);
+            }
+            return Response::Success(static::class .'::'. __FUNCTION__,
                 ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $info]]
             );
         }catch (\Exception $e) {
@@ -471,7 +481,7 @@ class Base {
      *
      * @return bool
      */
-    final public function exist() {
+    public function exist() {
         $where = $this->getWhereHooker();
         if(!$where) {
             return false;
@@ -481,16 +491,16 @@ class Base {
     /**
      * 保存数据
      *
-     * @return mixed|Response
+     * @return mixed|Qii\Driver\Response
      * @throws \Exception
      */
-    final public function add() {
+    public function add() {
         $valid = $this->validFieldsForAdd();
         if($valid->isError()) {
             return $valid;
         }
         //如果设置了 unique 就先验证唯一性
-        list($uniqueWhere, $exclude, $primary) = $this->condition();
+        list($uniqueWhere, $uniqueOr, $exclude, $primary) = $this->condition();
         unset($exclude);
 
         //如果 $where $or 为空,看看主键是否有设置值,有设置值说明验证主键就可以,反之设置了主键,主键值设置了,只验证主键就可以了
@@ -503,8 +513,8 @@ class Base {
             }
         }
 
-        if(count($uniqueWhere) > 0) {
-            $exist = $this->db()->limit(1)->where($uniqueWhere)->selectRow($this->getTable());
+        if(count($uniqueWhere) > 0 || count($uniqueOr) > 0) {
+            $exist = $this->db()->limit(1)->where($uniqueWhere)->orTerms($uniqueOr)->selectRow($this->getTable());
             if($exist) {
                 return Response::Exist(static::class .'::'. __FUNCTION__,
                     ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist]]
@@ -532,9 +542,7 @@ class Base {
      */
     public function remove() {
         if(!$this->properties()) {
-            return Response::FailRemove(static::class .'::'. __FUNCTION__,
-                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => '未指定删除属性,不允许删除', 'body' => 0]
-                ]);
+            return false;
         }
         $properties = [];
         foreach ($this->properties() as $key => $val) {
@@ -547,8 +555,8 @@ class Base {
         }
         $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::FailSave(static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => []]
                 ]);
         }
         return Response::Success(static::class .'::'. __FUNCTION__,
@@ -558,38 +566,46 @@ class Base {
     /**
      * 更新
      *
-     * @return mixed | Response
+     * @return mixed | Qii\Driver\Response
      * @throws \Exception
      */
-    final public function update(){
+    public function update(){
         $valid = $this->validFieldsForUpdate();
         if($valid->isError()) {
             return $valid;
         }
         //检查是否有重复的
-        list($uniqueWhere, $exclude, $primaryKey) = $this->condition();
+        list($uniqueWhere, $uniqueOr, $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());
+        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
+        /*print_r(
+            [
+                'where' => $uniqueWhere,
+                'or' => $uniqueOr,
+                'exclude' => $exclude,
+                'primary' => $primaryKey
+            ]
+        );*/
+        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
+
+        print_r(
+            [
+                'diffWhere' => $diffWhere,
+                'diffOr' => $diffOr,
+                'diffExclude' => $diffExclude
+            ]
+        );*/
+
+        if(count($diffWhere) > 0 || count($diffOr) > 0) {
+            $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->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) {
@@ -615,46 +631,46 @@ class Base {
     /**
      * 更新字段
      *
-     * @return mixed|Response
+     * @return mixed|\Qii\Driver\Qii\Driver\Response|Response
      * @throws \Exception
      */
-    final public function updateFields() {
+    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();
+        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
+        /*print_r(
+            [
+                'where' => $uniqueWhere,
+                'or' => $uniqueOr,
+                'exclude' => $exclude,
+                'primary' => $primaryKey
+            ]
+        );*/
         // 检查 unique 是否已经存在相关数据
         $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
+        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
+        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
 
-        if(count($diffWhere) > 0) {
-            $unique = $this->db()->limit(1)->where($diffWhere)->exclude($exclude)->selectRow($this->getTable());
+        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->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) {
@@ -681,12 +697,12 @@ class Base {
     /**
      * 增加或减少某一个字段的值
      *
-     * @return mixed|Response
+     * @return mixed|Qii\Driver\Response
      * @throws \Exception
      */
-    final public function incr() {
-        list($where, $exclude, $primary) = $this->condition();
-        unset($where, $exclude);
+    public function incr() {
+        list($where, $or, $exclude, $primary) = $this->condition();
+        unset($where, $or, $exclude);
         $property = $this->properties();
         $incr = [];
         foreach ($property as $key => $value) {
@@ -709,17 +725,6 @@ class Base {
                 $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()) {
@@ -735,15 +740,11 @@ class Base {
     /**
      * 获取第一条数据
      *
-     * @return mixed|Response
+     * @return mixed|\Qii\Driver\Response
      * @throws \Exception
      */
-    final public function first() {
+    public function first() {
         $orderBy = $this->getOrderBy();
-        //未设置order by 就以主键为准
-        if(empty($orderBy)) {
-            $orderBy = $this->primaryCondition();
-        }
         foreach ($orderBy as $key => $value) {
             $orderBy[$key] = 'ASC';
         }
@@ -751,6 +752,9 @@ class Base {
         if($this->db()->isError()) {
             return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
         }
+        if($row) {
+            $this->withRow($row);
+        }
         return Response::Success(static::class .'::'. __FUNCTION__,
             ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'Query 成功', 'body' => $row]]
         );
@@ -758,15 +762,11 @@ class Base {
     /**
      * 获取第一条数据
      *
-     * @return mixed|Response
+     * @return mixed|\Qii\Driver\Response
      * @throws \Exception
      */
-    final public function last() {
+    public function last() {
         $orderBy = $this->getOrderBy();
-        //未设置order by 就以主键为准
-        if(empty($orderBy)) {
-            $orderBy = $this->primaryCondition();
-        }
         foreach ($orderBy as $key => $value) {
             $orderBy[$key] = 'DESC';
         }
@@ -774,10 +774,48 @@ class Base {
         if($this->db()->isError()) {
             return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
         }
+        if($row) {
+            $this->withRow($row);
+        }
         return Response::Success(static::class .'::'. __FUNCTION__,
             ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => 'Query 成功', 'body' => $row]]
         );
     }
+
+    /**
+     * 附带一行数据
+     *
+     * @param $row
+     * @return void
+     */
+    public function withRow(&$row) {
+        if(!is_array($row)) {
+            return;
+        }
+        $hooks = $this->getWithHooker();
+        foreach ($hooks as $with) {
+            if(!is_array($with) || count($with) != 5 || !is_object($with[0])) {
+                continue;
+            }
+            $callable = $with[0];
+            $relField = $with[1];
+            $method = $with[2];
+            $foreignKey = $with[3];
+            $associationForeignkey = $with[4];
+            $row[$relField] = [];
+            if(!isset($row[$foreignKey])) {
+                continue;
+            }
+            $params = [$associationForeignkey,
+                [$associationForeignkey  => $row[$foreignKey]]
+            ];
+            $res = call_user_func_array(array($callable, $method), $params);
+
+            if($res && isset($res[$row[$foreignKey]])) {
+                $row[$relField] = $res[$row[$foreignKey]];
+            }
+        }
+    }
     /**
      * listts
      * @param int $page 页码
@@ -785,48 +823,107 @@ class Base {
      * @return array
      * @throws \Exception
      */
-    final public function lists($page = 1, $pageSize = 20) {
+    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());
+        $lists = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
+        $this->withList($lists);
+        $data['lists'] = $lists;
         return $data;
     }
+    /**
+     * 附带一行数据
+     *
+     * @param array $lists
+     * @return void
+     */
+    public function withList(&$lists) {
+        if(!is_array($lists)) {
+            return;
+        }
+        $hooks = $this->getWithHooker();
+        foreach ($hooks as $with) {
+            if(count($with) > 0 && is_object($with[0])) {
+                $callable = $with[0];
+                $relField = $with[1];
+                $method = $with[2];
+                $foreignKey = $with[3];
+                $associationForeignkey = $with[4];
 
+                $rel = [];
+                foreach ($lists as $v) {
+                    if(isset($v[$foreignKey])) {
+                        $rel[] = $v[$foreignKey];
+                    }
+                }
+                // [rel key, where]
+                $params = [$associationForeignkey,
+                    [$associationForeignkey . ':in' => array_unique($rel)]
+                ];
+                $res = call_user_func_array(array($with[0], $with[2]), $params);
+                foreach ($lists as &$v) {
+                    if(isset($res[$v[$foreignKey]])) {
+                        $v[$relField] = $res[$v[$foreignKey]];
+                    }
+                }
+            }
+        }
+    }
     /**
-     * 获取所有数据,这里不分页,但支持限条数,通过 setLimitHooker来实现
-     * $this->setLimitHooker(function(){
-     * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的
-     * });
-     * @return mixed
+     * 关联查询 has one
+     * params[rel_key, where]
+     *
+     * @return array
      * @throws \Exception
      */
-    final public function listAll() {
-        return $this->rsCondition()->selectRows($this->getTable());
+    public function hasOne() {
+        $args = func_get_args();
+        $relKey = $args[0];
+        $res = $this->db()->where($args[1])->rs($this->getTable());
+        $rows = [];
+        foreach($res as $k => $v) {
+            $rows[$v[$relKey]] = $v;
+        }
+        return $rows;
     }
+
     /**
-     * 返回指针
-     * @return mixed
+     * 关联查询
+     *  params[rel_key, where]
+     *
+     * @return array
      * @throws \Exception
      */
-    final public function rs() {
-        return $this->rsCondition()->rs($this->getTable());
+    public function hasMany()
+    {
+        $args = func_get_args();
+        $relKey = $args[0];
+        $res = $this->db()->where($args[1])->rs($this->getTable());
+        $rows = [];
+        foreach($res as $k => $v) {
+            $rows[$v[$relKey]][] = $v;
+        }
+        return $rows;
     }
-
     /**
-     * 通过此方法获取 rs
+     * 获取所有数据,这里不分页,但支持限条数,通过 setLimitHooker来实现
+     * $this->setLimitHooker(function(){
+     * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的
+     * });
      * @return mixed
+     * @throws \Exception
      */
-    private function rsCondition() {
+    public function listAll() {
         $limit = $this->getLimitHooker();
         if(empty($limit) || !is_array($limit)) {
-            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy());
+            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->selectRows($this->getTable());
         }
         if(count($limit) == 1) {
-            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0]);
+            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0])->selectRows($this->getTable());
         }
-        return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0], $limit[1]);
+        return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0], $limit[1])->selectRows($this->getTable());
     }
     /**
      * 验证相关字段
@@ -834,7 +931,7 @@ class Base {
      * @param array $fields 字段列表
      * @return Response
      */
-    final public function valid($fields = array()) {
+    public function valid($fields = array()) {
         if(!is_array($fields)) {
             return Response::FailValidate(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => '字段必须是数组', 'fields' => []]]);
         }
@@ -848,12 +945,9 @@ class Base {
                 if(!in_array($key, $fields)) {
                     continue;
                 }
-
                 $field = $this->entity()->convertToField($key);
-                $value = isset($values[$field]) ? $values[$field] : null;
-                $limit = isset($val[2]) ? $val[2]: '';
 
-                $verify = new Verify($val[0], $value, strtolower($valid), $val[1], $limit);
+                $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];
@@ -872,10 +966,10 @@ class Base {
     /**
      * 合并多个 valid 结果 $this->>valids($this->valid(['Uid']), $this->valid(['Nickname', 'Email']));
      * @param ...
-     * @return mixed|Response
+     * @return mixed|Qii\Driver\Response
      * @throws \Exception
      */
-    final public function valids() {
+    public function valids() {
         $validArr = func_get_args();
         if(count($validArr) == 0) {
             return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]);
@@ -912,37 +1006,28 @@ class Base {
      */
     protected function uniqueCondition() {
         $uniqueWhere = [];
+        $uniqueOr = [];
         $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;
+            $useWhere = false;
+            if(count($unique) == 1) {
+                $useWhere  = true;
             }
-
         }
-        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;
+
+        foreach ($unique as $key) {
+            $key = $this->entity()->convertToField($key);
+            $property = $this->entity()->convertToProperty($key);
+            if($useWhere) {
+                $uniqueWhere[$key] = $this->$property;
+            }else{
+                $uniqueOr[$key] = $this->$property;
             }
-            $uniqueWhere[] = $arr;
         }
-        return $uniqueWhere;
+        return [$uniqueWhere, $uniqueOr];
     }
 
     /**
@@ -990,13 +1075,13 @@ class Base {
      *
      * @return array [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition]
      */
-    final public function condition() {
+    public function condition() {
         //如果设置了 unique 就先验证唯一性,保存的时候验证 uniqueKey,更新的时候验证uniqueKey并排primaryKey
         //保存数据的时候验证uniqueKey;更新时验证 uniqueKey,并且排除 primaryKey,如果uniqueKey == primaryKey则不去做唯一性验证
-        $uniqueWhere = $this->uniqueCondition();
+        list($uniqueWhere, $uniqueOr) = $this->uniqueCondition();
         $excludeCondition = $this->excludeCondition();
         $primaryCondition = $this->primaryCondition();
-        return [$uniqueWhere, $excludeCondition, $primaryCondition];
+        return [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition];
     }
 
     /**
@@ -1004,11 +1089,11 @@ class Base {
      *
      * @return array
      */
-    final public function getOrderBy() {
+    public function getOrderBy() {
         $order = array();
-        $orderBy = (array) $this->orderBy();
+        $orderBy = $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']);
+            $orderBy = call_user_func($this->hooker['order']['func'], $order, $this->hooker['order']['args']);
         }
         foreach ($orderBy as $key => $val) {
             $field = $this->entity()->convertToField($key);
@@ -1018,7 +1103,7 @@ class Base {
             }
             $order[$field] = $val;
         }
-        return (array) $order;
+        return $order;
     }
 
     /**
@@ -1072,7 +1157,7 @@ class Base {
      *
      * @return mixed|\Qii\Driver\Qii\Driver\Response
      */
-    final public function response() {
+    public function response() {
         $properties = $this->properties();
         if(!$properties) {
             return Response::Fail(static::class .'::'. __FUNCTION__,
@@ -1121,7 +1206,7 @@ class Base {
                     (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]);
+                $func->set($key, $str, ['life_time' => $config['life_time']]);
                 return $res;
             }
         }

+ 17 - 127
src/Driver/Entity/Entity.php

@@ -2,7 +2,6 @@
 namespace Qii\Driver\Entity;
 
 use Qii\Driver\Model;
-use Qii\Driver\Response;
 
 /**
  * 通过数据表生成 entity
@@ -96,7 +95,7 @@ class Entity {
                 $doc[] = "\t * @var int ". $convertField . $comment;
                 $doc[] = "\t */";
             } else if (($tableInfo['rules']['maxlength'] && isset($tableInfo['rules']['maxlength'][$field]))
-                || ($tableInfo['rules']['text'] && in_array($field, $tableInfo['rules']['text']))
+                || (isset($tableInfo['rules']['text']) && in_array($field, $tableInfo['rules']['text']))
             ) {
                 $doc[] = "\t/**";
                 $doc[] = "\t * @var string ". $convertField . $comment;
@@ -109,10 +108,6 @@ class Entity {
                 $doc[] = "\t/**";
                 $doc[] = "\t * @var float ". $convertField . $comment;
                 $doc[] = "\t */";
-            }else if($tableInfo['rules']['decimal'] && isset($tableInfo['rules']['decimal'][$field])) {
-                $doc[] = "\t/**";
-                $doc[] = "\t * @var decimal ". $convertField . $comment;
-                $doc[] = "\t */";
             }
             if($tableInfo['rules']['default'] && isset($tableInfo['rules']['default'][$field])) {
                 $default = $tableInfo['rules']['default'][$field];
@@ -131,35 +126,7 @@ class Entity {
         $classAndProperty[] = "\n";
         $classAndProperty[] = "use Qii\Driver\Entity\Inf;";
         $classAndProperty[] = "use Qii\Driver\Entity\Base;";
-        $classAndProperty[] = "use Qii\Driver\Response;";
         $classAndProperty[] = "\n";
-        $classAndProperty[] = <<<DOC
-/**
- * @method \$this setWhereHooker(callable \$func)
- * @method \$this setOrderHooker(callable \$func)
- * @method \$this setLimitHooker(callable \$func)
- * @method \$this setCacheHooker(callable \$func)
- * @method \$this setQueryFieldsHooker(callable \$func)
- * @method \$this clear()
- * @method array properties()
- * @method bool exit()
- * @method \$this get()
- * @method Response info()
- * @method Response add()
- * @method Response remove()
- * @method Response update()
- * @method Response updateFields()
- * @method Response incr()
- * @method Response first()
- * @method Response last()
- * @method array lists(int \$page, int \$pageSize)
- * @method array listAll()
- * @method Object rs()
- * @method Response valids
- * @method array initPages(array \$data, int \$count, int \$page, int \$pageSize)
- */
-DOC;
-
         $classAndProperty[] = 'class '. $class . ' extends Base implements Inf{';
         $dynamicProperty = [];
         foreach ($properties as $property) {
@@ -186,6 +153,7 @@ DOC;
      * 动态变化的日期,初始化的时候赋值
      */
 DOC;
+            ;
             $classAndProperty[] = "\tpublic function __construct(){";
             $classAndProperty[] = "\n";
             $classAndProperty[] = "\t}";
@@ -215,7 +183,6 @@ DOC;
 \t}
 DOC;
 
-
         //生成 table rules
         $classAndProperty[] = $this->generateRules($tableInfo);
         $classAndProperty[] = '}';
@@ -231,11 +198,11 @@ DOC;
     public function rules() { 
 DOC;
         $constantJoin[] = "\t\t". '$rules = [];';
-        $next = array_reduce(array_reverse(['required', 'int', 'minlength', 'maxlength', 'decimal', 'timestamp', 'datetime']), function($carry, $item){
+        $next = array_reduce(array_reverse(['required', 'int', 'minlength', 'maxlength', 'timestamp', 'datetime']), function($carry, $item){
             return function() use ($carry, $item){
                 $tableInfo = func_get_args()[0];
                 $constantJoin = func_get_args()[1];
-                $currentRules = $tableInfo['rules'][$item];
+                $currentRules = $tableInfo['rules'][$item] ?? [];
                 $comments = $tableInfo['comment'];
                 $join = [];
                 switch ($item) {
@@ -245,9 +212,6 @@ DOC;
                     case 'int':
                         $join = $this->generateInt($currentRules, $comments);
                         break;
-                    case 'decimal':
-                        $join = $this->generateDecimal($currentRules, $comments);
-                        break;
                     case 'minlength':
                         $join = $this->generateMinLength($currentRules, $comments);
                         break;
@@ -267,55 +231,21 @@ DOC;
             return $constantJoin;
         })($tableInfo, $constantJoin);
 
+        $primaryKey = [];
+        if(isset($tableInfo['rules']['extra']) && isset($tableInfo['rules']['extra']['auto_increment'])) {
+            $primaryKey = $tableInfo['rules']['extra']['auto_increment'];
+        }
         $next[] = "\t\treturn ". '$rules;';
         $next[] = "\t". '}';
-        //主键自动添加
-        $primaryKey = array();
-        if(isset($tableInfo['rules']['pri'])) {
-            $primaryKey = $tableInfo['rules']['pri'];
-        }
-        //保存验证的时候使用 required 并去掉pri和default里边的内容
-        $defaults = array();
-        if(isset($tableInfo['rules']['default'])) {
-            $defaults = array_keys($tableInfo['rules']['default']);
-        }
-        $validForSave = array();
-        if(isset($tableInfo['rules']) && isset($tableInfo['rules']['required'])) {
-            foreach ($tableInfo['rules']['required'] as $field) {
-                if(!in_array($field, $primaryKey) && !in_array($field, $defaults)) {
-                    $validForSave[] = $this->convertToProperty($field);
-                }
-            }
-        }
-        $uniKey = array();
-        if(isset($tableInfo['rules']) && isset($tableInfo['rules']['uniq'])) {
-            foreach ($tableInfo['rules']['uniq'] as $uniq) {
-                $uniKey[] = $this->convertToProperties($uniq);
-            }
-        }
-
         $next[] = <<<DOC
     /**
      * unique (unique 如果是 array 则表示 联合唯一,如果是string则是任意唯一,多个单独唯一使用字符串以逗号隔开, 主键除外)
-     * array(array1, array2) , array1 和array 2单独唯一
+     *
      * @return mixed
      * @throws \Exception
      */
     public function uniqueKey(){
-DOC;
-        if(count($uniKey) > 0) {
-            $uniq = array();
-            foreach ($uniKey as $val) {
-                $uniq[] = "array('". join("','", $val). "')";
-            }
-            $next[] = "\t\treturn array(". join(", ", $uniq). ");";
-        }else{
-            $next[] = <<<DOC
-        return array();
-DOC;
-        }
-
-        $next[] = <<<DOC
+        throw new \Exception('请设置唯一值');
     }
 
     /**
@@ -326,10 +256,8 @@ DOC;
      */
     public function primaryKey(){
 DOC;
-        $exclude = "\t\treturn array();";
         if($primaryKey) {
-            $next[] = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
-            $exclude = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
+            $next[] = "\t\treturn array('". join("','", $primaryKey). "');";
         }else{
             $next[] = <<<DOC
         throw new \Exception('请设置主键');
@@ -340,37 +268,30 @@ DOC;
     }
 
     /**
-     * 更新数据的时候,验证唯一需要排除的值,此处仅支持,单个或联合排除,不支持单个排除
+     * 保存数据的时候,验证唯一需要排除的值,此处仅支持,单个或联合排除,不支持单个排除
      *
      * @return array
      */
     public function exclude(){
-DOC;
-        $next[] = $exclude;
-        $next[] = <<<DOC
+        return array();
     }
     /**
      * 添加时验证的字段,自行添加
      */
     public function validFieldsForAdd(){
-DOC;
-        if(count($validForSave) > 0) {
-            $next[] = "\t\$fields = array('". join("', '", $validForSave)."');";
-        }else{
-            $next[] = "\t\$fields = array();";
-        }
-        $next[] = <<<DOC
+        \$fields = [];
         return \$this->valid(\$fields);
     }
     
     /**
-     * 更新时验证的字段,自行添加
+     * 验证更新时的字段,自行添加
      */
     public function validFieldsForUpdate() {
-        \$fields = array();
+        \$fields = [];
         return \$this->valid(\$fields);
     }
 DOC;
+
         return join("\n", $next);
     }
 
@@ -474,13 +395,6 @@ DOC;
         $constantJoin[] = "\t\t];";
         return $constantJoin;
     }
-
-    /**
-     * int 类型数据
-     * @param $rules
-     * @param $comments
-     * @return array
-     */
     public function generateInt($rules, $comments = array()) {
         $constantJoin = [];
 
@@ -521,30 +435,6 @@ DOC;
 
         return array_merge($constantJoin, $intProperty['min'], $intProperty['max'], $intProperty['number']);
     }
-    public function generateDecimal($rules, $comments = array()) {
-        $constantJoin = [];
-
-        if(!$rules || !is_array($rules)) {
-            return $constantJoin;
-        }
-        $constantJoin = [];
-        $constantJoin[] = "\t\t". '$rules["decimal"] = [';
-        $joinProperty = [];
-        foreach ($rules as $key => $val) {
-            $comment = '';
-            if(isset($comments[$key]) && $comments[$key] != "") {
-                $comment = $comments[$key];
-            }
-
-            $message = ($comment != '' ? $comment : $key) . "格式示例: ". str_repeat(6, min(1, (int)$val[0] - (int)$val[1])) . ".". str_repeat(6, (int)$val[1]);
-            $joinProperty[] = <<<DOC
-    \t\t["{$this->convertToProperty($key)}", "{$message}", "{$val[0]},{$val[1]}"]
-DOC;
-        }
-        $constantJoin[] = join(",\n", $joinProperty);
-        $constantJoin[] = "\t\t];";
-        return $constantJoin;
-    }
     /**
      * 转换为属性,属性会将下划线+小写/大写 转换成大写
      *

+ 1 - 1
src/Driver/Model.php

@@ -36,7 +36,7 @@ class Model
      */
     protected $_allow = array('pdo', 'mysql', 'mysqli');
     /**
-     * @var object $db 数据库实例
+     * @var Base $db 数据库实例
      */
     public $db = null;
     /**

+ 1 - 1
src/Qii.php

@@ -157,7 +157,7 @@ class Qii extends Application
      * @param Int|String $code
      * @return Bool
      */
-    public static function setError($condition, $line = 0, $code = 0, $args = null, $msg = '')
+    public static function setError($condition, $line, $code = 0, $args = null, $msg = '')
     {
         return call_user_func_array(array('\Qii\Exceptions\Error', 'setError'), func_get_args());
     }

+ 8 - 2
src/Router/Parse/Normal.php

@@ -183,9 +183,15 @@ class Normal
                 $maxIndex++;
             }
         }
-        list($controller, $action) = explode(":", $rulesVal);
+
+        $explodeRules = explode(":", $rulesVal);
+        if(count($explodeRules) < 2) {
+            $explodeRules = array_pad($explodeRules, 2, '');
+        }
+        $controller = $explodeRules[0];
+        $action = $explodeRules[1];
         $match['controller'] = $controller;
-        $match['action'] = $action ? $action: Register::get("APP_DEFAULT_ACTION");
+        $match['action'] = $action != '' ? $action: Register::get("APP_DEFAULT_ACTION");
         $match['replacements'] = $replacements;
         $match['rulesVal'] = $rulesVal;