瀏覽代碼

Add Entity 使操作数据库更简单

朱金辉 1 年之前
父節點
當前提交
2711e3ade8

+ 33 - 12
src/Application.php

@@ -9,6 +9,7 @@ use \Qii\Autoloader\Import;
 use \Qii\Autoloader\Psr4;
 use \Qii\Autoloader\Instance;
 
+use Qii\Cache\Loader;
 use \Qii\Config\Register;
 use \Qii\Config\Consts;
 use \Qii\Config\Setting;
@@ -126,7 +127,7 @@ class Application
      */
     public function setAppIniFile($iniFile)
     {
-        Register::set(Consts::APP_INI_FILE, $iniFile);
+        Register::set(Consts::APP_INI_FILE, self::realpath($iniFile));
         return $this;
     }
 
@@ -209,9 +210,9 @@ class Application
     public function setAppConfigure($ini, $env = '')
     {
         if ($env == '') $env = $this->getEnv();
-        $ini = Psr4::getInstance()->getFileByPrefix($ini);
-        $this->setAppIniFile($ini);
-        if (!Register::setAppConfigure($ini, $env)) throw new FileNotFound($ini, 404);
+        $realpath = self::realpath($ini);
+        $this->setAppIniFile($realpath);
+        if (!Register::setAppConfigure($realpath, $env)) throw new FileNotFound($ini, 404);
         //载入request方法
         $this->request = $this->getRequest();
         $this->dispatcher = $this->getDispatcher();
@@ -235,8 +236,8 @@ class Application
      */
     public function mergeAppConfigure($iniFile, $array)
     {
-        if (!is_array($array)) return;
-        Register::mergeAppConfigure($iniFile, $array);
+        if (!is_array($array)) return $this;
+        Register::mergeAppConfigure(self::realpath($iniFile), $array);
         return $this;
     }
 
@@ -249,7 +250,7 @@ class Application
      */
     public function rewriteAppConfigure($iniFile, $key, $val)
     {
-        Register::rewriteConfig($iniFile, $key, $val);
+        Register::rewriteConfig(self::realpath($iniFile), $key, $val);
         return $this;
     }
 
@@ -371,7 +372,7 @@ class Application
         if ($basicPolicy['servers']) {
             $policy = array_merge($basicPolicy, $policy);
         }
-        $loader = new \Qii\Cache\Loader($engine);
+        $loader = new Loader($engine);
         return $loader->initialization($policy);
     }
 
@@ -433,13 +434,13 @@ class Application
      */
     public function setDBIniFile($iniFile)
     {
-        Register::set(Consts::APP_DB, $iniFile);
+        Register::set(Consts::APP_DB, self::realpath($iniFile));
     }
 
     /**
      * 获取当前数据库文件
      *
-     * @return \Qii\Mix
+     * @return mixed
      * @throws \Qii\Exceptions\Variable
      */
     public function getDBIniFile()
@@ -455,9 +456,11 @@ class Application
     public function setDB($ini, $env = '')
     {
         if ($env == '') $env = $this->getEnv();
-        $this->setDBIniFile($ini);
+        $realpath = self::realpath($ini);
+
+        $this->setDBIniFile($realpath);
         if (!Register::setAppConfigure(
-            Psr4::getInstance()->getFileByPrefix($ini),
+            Psr4::getInstance()->getFileByPrefix($realpath),
             $env)
         ) {
             throw new FileNotFound($ini, 404);
@@ -506,6 +509,24 @@ class Application
     }
 
     /**
+     * 获取ini文件的真实路径
+     *
+     * @param string $iniFile ini 文件
+     * @return string
+     * @throws FileNotFound
+     */
+    protected static function realpath($iniFile) {
+        if(isset(self::$config[$iniFile])) return self::$config[$iniFile];
+        $ini = Psr4::getInstance()->getFileByPrefix($iniFile);
+        $realpath = realpath($ini);
+        if(!$realpath) {
+            throw new FileNotFound($ini, 404);
+        }
+        self::$config[$iniFile] = $realpath;
+        return $realpath;
+    }
+
+    /**
      * 执行
      * @return $this
      * @throws \Exception

+ 24 - 0
src/Base/Request.php

@@ -84,6 +84,30 @@ abstract class Request
         return call_user_func_array(array($this->url, 'post'), func_get_args());
     }
 
+
+    /**
+     * get key是否存在
+     * @param string $key
+     * @return bool
+     */
+    public function hasGetKey($key) {
+        if($_GET && isset($_GET[$key])) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * post key是否存在
+     * @param string $key
+     * @return bool
+     */
+    public function hasPostKey($key) {
+        if($_POST && isset($_POST[$key])) {
+            return true;
+        }
+        return false;
+    }
     /**
      * 默认controller
      */

+ 44 - 34
src/Config/Register.php

@@ -132,10 +132,10 @@ class Register
 	/**
 	 * 读取ini配置文件
 	 *
-	 * @param String $fileName
-	 * @return Array
+	 * @param string $fileName
+	 * @return array
 	 */
-	public static function ini($fileName)
+	public static function parseIni($fileName)
 	{
 		if (!$fileName) throw new Variable(\Qii::i(1408), __LINE__);
 		$ini = parse_ini_file($fileName, true);
@@ -179,13 +179,18 @@ class Register
 	 */
 	public static function getCacheName($iniFile)
 	{
+        $refClass = new \ReflectionClass('\Qii\Config\Consts');
+        $constants = array_values($refClass->getConstants());
+        if(in_array($iniFile, $constants)) {
+            return $iniFile;
+        }
 		$cacheName = basename($iniFile);
 		$environs = Register::get(Consts::APP_ENVIRONS, array());
 		if (isset($environs[$cacheName])) {
 			$environ = $environs[$cacheName];
 			$cacheName = $environ . '.' . $cacheName;
 		}
-		return $cacheName;
+		return $cacheName .'.'. md5($iniFile);
 	}
 
 	/**
@@ -247,8 +252,7 @@ class Register
 		$environs = Register::get(Consts::APP_ENVIRONS, array());
 		$environs[$cacheName] = $environ;
 		Register::set(Consts::APP_ENVIRONS, $environs);
-
-		$cacheName = $environ . '.' . $cacheName;
+        $cacheName = Register::getCacheName($iniFile);
 		if (!is_file($iniFile)) return false;
 		$cacheFile = Psr4::getInstance()->getFileByPrefix(Register::get(Consts::APP_CACHE_PATH) . DS . $cacheName . '.php');
 		if (self::$isWrite && Register::get(Consts::APP_CACHE_PATH)) {
@@ -260,7 +264,7 @@ class Register
 				}
 			}
 		}
-		$array = Register::ini($iniFile);
+		$array = Register::parseIni($iniFile);
 		if (!$array) return false;
 		$common = $array['common'];
 		if (isset($array[$environ])) {
@@ -283,31 +287,36 @@ class Register
      * @param array $array2
      * @return array
      */
-    public static function array_merge_recursive_distinct ( array &$array1, array &$array2 )
+    /**
+     * 递归的合并两个数组 array_merge_recursive会把字段转化成数组
+     *
+     * @param array $array1
+     * @param array $array2
+     * @return array
+     */
+    public static function array_merge_recursive_distinct(array &$array1, array &$array2)
     {
         $merged = $array1;
-
-        foreach ( $array2 as $key => &$value )
+        foreach ($array2 as $key => &$value)
         {
-            if ( is_array ( $value ) && isset ( $merged [$key] ) && is_array ( $merged [$key] ) )
+            if (is_array($value) && isset ($merged[$key]) && is_array($merged[$key]))
             {
-                $merged [$key] = self::array_merge_recursive_distinct ( $merged [$key], $value );
+                $merged[$key] = Register::array_merge_recursive_distinct($merged [$key], $value );
             }
             else
             {
-                $merged [$key] = $value;
+                $merged[$key] = $value;
             }
         }
-
         return $merged;
     }
-	/**
-	 * 设置网站的配置文件
-	 *
-	 * @param String $iniFile 配置我呢见
-	 * @param string $environ 环境变量
-	 * @return Object self
-	 */
+    /**
+     * 设置网站的配置文件
+     *
+     * @param String $iniFile 配置我呢见
+     * @param string $environ 环境变量
+     * @return array
+     */
 	public static function setAppConfigure($iniFile, $environ = 'product')
 	{
 		return Register::setConfig($iniFile, $environ);
@@ -316,28 +325,28 @@ class Register
 	/**
 	 * 获取配置ini文件相关信息
 	 * @param String $fileName 文件名
-	 * @return Ambigous <Mix, multitype:>
+	 * @return mixed
 	 */
 	public static function getIniConfigure($fileName)
 	{
-		$cacheName = basename($fileName);
-		$environs = Register::get(Consts::APP_ENVIRONS, array());
-		if (isset($environs[$cacheName])) {
-			$cacheName = $environs[$cacheName] . '.' . $cacheName;
-		}
+        // @todo cache name 不统一
+        $cacheName = Register::getCacheName($fileName);
 		return Register::get($cacheName);
 	}
 
 	/**
 	 * 获取网站的配置信息
 	 *
-	 * @return Array|mixed
+	 * @return mixed
 	 */
-	public static function getAppConfigure($iniFile = Consts::APP_INI, $key = NULL)
+	public static function getAppConfigure($iniFile = Consts::APP_INI, $key = null)
 	{
 		$appConfigure = Register::getIniConfigure($iniFile);
 		if ($key == null) return $appConfigure;
-		return isset($appConfigure[$key]) ? $appConfigure[$key] : NULL;
+        if(isset($appConfigure[$key])) {
+            return $appConfigure[$key];
+        }
+		return null;
 	}
 
 	/**
@@ -356,13 +365,14 @@ class Register
 
 	/**
 	 * 获取当前系统环境
-	 * @return Ambigous <Mix, multitype:>
+	 * @return string
 	 */
 	public static function getAppEnviron()
 	{
-		return isset(Register::$_cache[Consts::APP_ENVIRON]) ?
-                    Register::$_cache[Consts::APP_ENVIRON]
-                    : Consts::APP_DEFAULT_ENVIRON;
+        if(isset(Register::$_cache[Consts::APP_ENVIRON])) {
+            return Register::$_cache[Consts::APP_ENVIRON];
+        }
+        return Consts::APP_DEFAULT_ENVIRON;
 	}
 
 	public function __call($method, $argvs)

+ 21 - 15
src/Driver/Base.php

@@ -4,6 +4,7 @@ use Qii\Autoloader\Psr4;
 use Qii\Exceptions\InvalidFormat;
 use Qii\Exceptions\InvalidParams;
 use Qii\Exceptions\TableException;
+use Qii\Exceptions\Variable;
 
 /**
  * Class Base
@@ -26,8 +27,8 @@ use Qii\Exceptions\TableException;
  *           $this->db->where(array('uid:greater' => 1, 'or', 'email:like' => 'test@test.com'))->selectAll('user');
  *           $this->db->where(array('uid:greater' => 1))->where("OR")->like(array('email' => 'test@test.com'))->selectAll('user');
  *           $this->db->where(array('name' => 'antsnet'))->exclude(array('email' => 'test@test.com', 'status' => 1))->selectAll('user');
- *           $this->db->where(array('name' => 'antsnet'))->or(array('email' => 'test@test.com', 'status' => 1))->selectAll('user');
- *           $this->db->or(array('email' => 'test@test.com', 'status' => 1))->selectAll('user');
+ *           $this->db->where(array('name' => 'antsnet'))->orTerms(array('email' => 'test@test.com', 'status' => 1))->selectAll('user');
+ *           $this->db->orTerms(array('email' => 'test@test.com', 'status' => 1))->selectAll('user');
  *           $this->db->join(array("leftJoin", array("table" => 'table', 'alias' => 'a', 'on' => 'a.id=b.id')));
  *           $this->db->join(" LEFT JOIN table c on c.id=a.id")->selectAll('use a');
  *           $this->db->where(array('email:unequal' => 'test@test.com'))->in(array('uid:in' => array('1,2,3'), 'status' => array(1)))->selectAll('user');
@@ -162,7 +163,7 @@ class Base
         $replaceObj = $this->createInsertReplaceObj($dataArray);
         if(empty($replaceObj['fields']) || empty($replaceObj['values']))
         {
-            throw new \Qii\Exceptions\Variable(_i('Invalid %s format', 'data'), __LINE__);
+            throw new Variable(_i('Invalid %s format', 'data'), __LINE__);
         }
         $this->modelSQL = $sql = "INSERT INTO " . $this->getTable($table) . "(`" . join("`, `", $replaceObj['fields']) . "`) VALUES('" . join("', '", $replaceObj['values']) . "')";
         $this->setQuery($sql);
@@ -183,7 +184,7 @@ class Base
         $replaceObj = $this->createInsertReplaceObj($dataArray);
         if(empty($replaceObj['fields']) || empty($replaceObj['values']))
         {
-            throw new \Qii\Exceptions\Variable(_i('Invalid %s format', 'data'), __LINE__);
+            throw new Variable(_i('Invalid %s format', 'data'), __LINE__);
         }
         $this->modelSQL = $sql = "REPLACE INTO " . $this->getTable($table) . "(`" . join("`, `", $replaceObj['fields']) . "`) VALUES('" . join("', '", $replaceObj['values']) . "')";
         $rs = $this->setQuery($sql);
@@ -697,7 +698,7 @@ class Base
     /**
      * 清空Data数据
      */
-    final public function clearCondition()
+    final public function cleanCondition()
     {
         $this->fields = '*';
         $this->whereCondition = array();
@@ -803,7 +804,7 @@ class Base
         //$this->limit = null;
 
         $sql = sprintf($this->_query['SELECT'], $fields, $aliases['name'], $aliases['alias']) . $join . $where . $groupBy . $orderBy . $limit;
-        $this->clearCondition();
+        $this->cleanCondition();
         return $sql;
     }
     /**
@@ -933,24 +934,30 @@ class Base
         {
             $isOperator = $this->isOperator($val);//如果是操作符,上一个不是操作符就清空tmpWhere,并放入slices
             $isValue = !$isOperator;
-            /*if($isOperator) {
-                $hasOperator = true;
-            }*/
             if($lastIsValue && $isOperator)
             {
-                $slices[] = $tmpWhere;
+                $slices[] = array_values($tmpWhere);
                 $tmpWhere = array();
-                $tmpWhere[] = $val;
+                $tmpWhere[$index] = $val;
             }
             else if($count - 1 == $index)
             {
-                $tmpWhere[] = $val;
-                $slices[] = $tmpWhere;
+                $tmpWhere[$index] = $val;
+                $slices[] = array_values($tmpWhere);
+                unset($tmpWhere);
             }else {
-                $tmpWhere[] = $val;
+                $tmpWhere[$index] = $val;
             }
             $lastIsValue = $isValue;
         }
+        // @todo 待测试
+        if(count($tmpWhere) > 0) {
+            //作为独立 slices 的条件
+            if(count($slices) > 0) {
+                $slices[] = "and";
+            }
+            $slices[][] = $tmpWhere;
+        }
         return $slices;
     }
     /**
@@ -1045,7 +1052,6 @@ class Base
             $lastIsOperator = $isOperator;
             $i++;
         }
-
         return join(" ", $where);
     }
 

+ 722 - 0
src/Driver/Entity/Base.php

@@ -0,0 +1,722 @@
+<?php
+namespace Qii\Driver\Entity;
+
+
+
+use Qii\Driver\Response;
+
+class Base {
+    public static $propertys;
+    public function __construct(){
+    }
+    public function defaultFieldsValue(){}
+    public function getTable() {
+        throw new \Exception('请先设置需要保存的数据表');
+    }
+
+    /**
+     * 返回使用的db信息
+     *
+     * @return mixed
+     */
+    public function db() {
+        return _loadClass('\Qii\Driver\Model');
+    }
+
+    /**
+     * 返回 entity 信息
+     * @return mixed
+     */
+    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);
+    }
+    public function validFieldsForAdd(){
+        throw new \Exception('请设置插入数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
+    }
+
+    public function validFieldsForUpdate() {
+        throw new \Exception('请设置更新数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
+    }
+    /**
+     * 获取不为空的属性
+     *
+     * @return array
+     */
+    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
+     */
+    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() {
+        return $this->db()->fields(' COUNT(1) as count')->where($this->properties())->selectOne($this->getTable());
+    }
+
+    /**
+     * 检查是否有对应的属性
+     *
+     * @param $key
+     * @return bool
+     */
+    public function hasProperty($key) {
+        $class = get_called_class();
+        $key = $this->entity()->convertToProperty($key);
+        if(isset(self::$propertys[$class])) {
+            return isset(self::$propertys[$class][$key]);
+        }
+        $method = new \ReflectionClass($class);
+        $properties = $method->getproperties();
+        $fields = [];
+        foreach($properties as $property) {
+            if($property->isPublic()) {
+                $name = $property->getName();
+                $fields[$name] = '';
+            }
+        }
+        self::$propertys[$class] = $fields;
+        return isset($fields[$key]);
+    }
+
+    /**
+     * bind value
+     * @param $values
+     * @return $this|false
+     */
+    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 mixed
+     * @throws \Exception
+     */
+    public function get() {
+        $info = $this->db()->where($this->properties())->selectRow($this->getTable());
+        $class = get_called_class();
+        $obj = new $class();
+        if(!$info) {
+            return $obj;
+        }
+        foreach ($info as $key => $val) {
+            $key = $this->entity()->convertToProperty($key);
+            $obj->$key = $val;
+        }
+        return $obj;
+    }
+
+
+    /**
+     * 是否相关数据存在
+     *
+     * @return bool
+     */
+    public function exist() {
+        if(!$this->properties()) {
+            return false;
+        }
+        return (bool) $this->db()->where($this->properties())->selectOne($this->getTable());
+    }
+    /**
+     * 保存数据
+     *
+     * @return mixed|Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function add() {
+        $valid = $this->validFieldsForAdd();
+        if($valid->isError()) {
+            return $valid;
+        }
+        //如果设置了 unique 就先验证唯一性
+        list($uniqueWhere, $uniqueOr, $exclude, $primary) = $this->condition();
+        unset($exclude);
+
+        //如果 $where $or 为空,看看主键是否有设置值,有设置值说明验证主键就可以,反之设置了主键,主键值设置了,只验证主键就可以了
+        if(count($primary) > 0) {
+            $exist = $this->db()->limit(1)->where($primary)->selectRow($this->getTable());
+            if($exist) {
+                return Response::Exist(static::class .'::'. __FUNCTION__,
+                    ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist]]
+                );
+            }
+        }
+
+        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]]
+                );
+            }
+        }
+
+        $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 mixed | Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function update(){
+        $valid = $this->validFieldsForUpdate();
+        if($valid->isError()) {
+            return $valid;
+        }
+        return $this->updateFields();
+        //检查是否有重复的
+        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
+        // 检查 unique 是否已经存在相关数据
+        $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
+        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
+        /*print_r(
+            [
+                'where' => $uniqueWhere,
+                'or' => $uniqueOr,
+                'exclude' => $exclude,
+                'primary' => $primaryKey
+            ]
+        );*/
+        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
+
+        print_r(
+            [
+                'diffWhere' => $diffWhere,
+                'diffOr' => $diffOr,
+                'diffExclude' => $diffExclude
+            ]
+        );*/
+
+        if(count($diffWhere) > 0 || count($diffOr) > 0) {
+            $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->getTable());
+            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->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(), $values, $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]]
+        );
+    }
+
+    public function updateFields() {
+        $properties = $this->properties();
+        $fields = $this->entity()->convertToProperties(array_keys($properties));
+        $valid = $this->valid($fields);
+        if($valid->isError()) {
+            return $valid;
+        }
+        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
+        /*print_r(
+            [
+                'where' => $uniqueWhere,
+                'or' => $uniqueOr,
+                'exclude' => $exclude,
+                'primary' => $primaryKey
+            ]
+        );*/
+        // 检查 unique 是否已经存在相关数据
+        $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
+        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
+        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
+
+        print_r(
+            [
+                'diffWhere' => $diffWhere,
+                'diffOr' => $diffOr,
+                'diffExclude' => $diffExclude
+            ]
+        );*/
+
+        if(count($diffWhere) > 0 || count($diffOr) > 0) {
+            $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->getTable());
+            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->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(), $values, $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|Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function incr() {
+        list($where, $or, $exclude, $primary) = $this->condition();
+        unset($where, $or, $exclude);
+        $property = $this->properties();
+        $incr = [];
+        foreach ($property as $key => $value) {
+            if(!is_numeric($value)) {
+                continue;
+            }
+            $incr[$key] = $value;
+        }
+        $diff = array_diff_assoc($incr, $primary);
+        if(count($diff) == 0) {
+            return Response::Fail(self::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::DO_FAIL, 'msg' => 'INCR 参数错误']]);
+        }
+        $sets = [];
+        foreach ($diff as $key => $val) {
+            if($val == 0) {
+                $sets[$key] = $val;
+            }else if($val > 0) {
+                $sets[$key . ':plus'] = $val;
+            }else if($val < 0) {
+                $sets[$key . ':minus'] = $val * -1;
+            }
+        }
+        $affectedRows = $this->db()->set($sets)->where($primary)->update($this->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|\Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function first() {
+        $orderBy = $this->getOrderBy();
+        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|\Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function last() {
+        $orderBy = $this->getOrderBy();
+        foreach ($orderBy as $key => $value) {
+            $orderBy[$key] = 'DESC';
+        }
+        $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]]
+        );
+    }
+    /**
+     * listts
+     * @param int $page 页码
+     * @param int $pageSize 页大小
+     * @return array
+     * @throws \Exception
+     */
+    public function lists($page = 1, $pageSize = 20) {
+        $count = $this->count();
+        if(!$this->initPages($data, $count, $page, $pageSize)) {
+            return $data;
+        }
+        $order = $this->getOrderBy();
+        $data['lists'] = $this->db()->where($this->properties())->orderBy($order)->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
+
+        return $data;
+    }
+
+    /**
+     * 验证相关字段
+     *
+     * @param array $fields 字段列表
+     * @return Response
+     */
+    public function valid($fields = array()) {
+        if(!is_array($fields)) {
+            return Response::FailValidate(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => '字段必须是数组', 'fields' => []]]);
+        }
+        $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|Qii\Driver\Response
+     * @throws \Exception
+     */
+    public function valids() {
+        $validArr = func_get_args();
+        if(count($validArr) == 0) {
+            return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]);
+        }
+        if(count($validArr) == 1) {
+            return $validArr[0];
+        }
+
+        $invalid = array();
+        $invalid['message'] = array();
+        $invalid['fields'] = array();
+
+        foreach ($validArr as $valid) {
+            if(!($valid instanceof Response)) {
+                throw new \Exception('验证结果类型必须是\Qii\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 = [];
+        $uniqueOr = [];
+        $unique = $this->uniqueKey();
+        $useWhere = true;
+
+        if(!is_array($unique)) {
+            $unique = $this->explode(',', $unique);
+            $useWhere = false;
+            if(count($unique) == 1) {
+                $useWhere  = true;
+            }
+        }
+
+        foreach ($unique as $key) {
+            $key = $this->entity()->convertToField($key);
+            $property = $this->entity()->convertToProperty($key);
+            if($useWhere) {
+                $uniqueWhere[$key] = $this->$property;
+            }else{
+                $uniqueOr[$key] = $this->$property;
+            }
+        }
+        return [$uniqueWhere, $uniqueOr];
+    }
+
+    /**
+     * exclude Condition
+     *
+     * @return array
+     */
+    protected function excludeCondition() {
+        if(is_array($this->exclude())) {
+            $exclude = $this->exclude();
+        }else{
+            $exclude = (array) $this->explode(',', $this->exclude());
+        }
+        $excludeCondition = [];
+        foreach ($exclude as $key) {
+            $field = $this->entity()->convertToField($key);
+            $property = $this->entity()->convertToProperty($key);
+            $excludeCondition[$field] = $this->$property;
+        }
+        return $excludeCondition;
+    }
+
+    /**
+     * 主键
+     *
+     * @return array
+     * @throws \Exception
+     */
+    protected function primaryCondition(){
+        $primary = array();
+        $primaryKey = $this->primaryKey();
+        if(!is_array($primaryKey)) {
+            $primaryKey = $this->explode(',', $primaryKey);
+        }
+        foreach ($primaryKey as $key) {
+            $key = $this->entity()->convertToProperty($key);
+            $field = $this->entity()->convertToField($key);
+            $value = $this->$key;
+            $primary[$field] = $value;
+        }
+        return $primary;
+    }
+    /**
+     * 获取查询条件
+     *
+     * @return array [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition]
+     */
+    public function condition() {
+        //如果设置了 unique 就先验证唯一性,保存的时候验证 uniqueKey,更新的时候验证uniqueKey并排primaryKey
+        //保存数据的时候验证uniqueKey;更新时验证 uniqueKey,并且排除 primaryKey,如果uniqueKey == primaryKey则不去做唯一性验证
+        list($uniqueWhere, $uniqueOr) = $this->uniqueCondition();
+        $excludeCondition = $this->excludeCondition();
+        $primaryCondition = $this->primaryCondition();
+        return [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition];
+    }
+
+    /**
+     * 获取order by
+     *
+     * @return array
+     */
+    public function getOrderBy() {
+        $order = array();
+        $orderBy = $this->orderBy();
+        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 $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;
+    }
+
+    /**
+     * 默认转发到 db model上
+     * @param string $method method
+     * @param array $args
+     * @return mixed
+     */
+    public function __call($method, $args) {
+        return call_user_func_array(array($this->db(), $method), $args);
+    }
+}

+ 451 - 0
src/Driver/Entity/Entity.php

@@ -0,0 +1,451 @@
+<?php
+namespace Qii\Driver\Entity;
+
+use Qii\Driver\Model;
+
+/**
+ * 通过数据表生成 entity
+ * 使用方法:
+ * use Qii\Driver\Entity\Entity;
+ * $entity = new Entity();
+ * $class = $entity->generateProperties($table);
+ * file_put_contents('entity/'. $table . '.php', $class);
+ *
+ * $fields = $entity->generateField('\entity\'. $table);
+ * print_r($fields);
+ *
+ * entity 生成规则:
+ * 首字母、下划线+字母 -> 大写字母
+ */
+class Entity {
+    /**
+     * @var bool $ucFirst 首字母是否大写
+     */
+    public $ucFirst = true;
+    public $namespace = "entity";
+    public function __construct(){
+    }
+
+    /**
+     * 设置 entity 的名字空间
+     * @param string $namespace 名字空间
+     * @return void
+     */
+    public function setNamespace($namespace) {
+        $this->namespace = $namespace;
+    }
+
+    /**
+     * 通过 Entity 获取数据表的字段
+     *
+     * @param $class
+     * @return array
+     * @throws \ReflectionException
+     */
+    public function generateField($class) {
+        if(!$class) {
+            throw new \Exception('Class is null');
+        }
+        if(class_exists($class, false)) {
+            throw new \Exception('Class '. $class .' does not exist');
+        }
+        $method = new \ReflectionClass($class);
+        $properties = $method->getProperties();
+        $fields = [];
+        foreach($properties as $property) {
+            if($property->isPublic()) {
+                $fields[] = $this->convertToField($property->getName());
+            }
+        }
+        return $fields;
+    }
+
+    /**
+     * 将数据表生成entity
+     *
+     * @param string $table 表名
+     * @return string
+     * @throws \Exception
+     */
+    public function generateProperties($table, $className = '') {
+        $db = new Model();
+        $tableInfo = $db->getTableInfo($table);
+        if($db->isError()) {
+            throw new \Exception($db->getError());
+        }
+        if(!$tableInfo || empty($tableInfo['fields'])) {
+            throw new \Exception($table . ' does not have any field');
+        }
+        $properties = [];
+        $docs = [];
+        $defaults = [];
+        foreach ($tableInfo['fields'] as $field) {
+            $comment = $tableInfo['comment'][$field] ?? '';
+            $convertField = $this->convertToProperty($field);
+            $comment = strlen($comment) > 0 ? " ". $comment : "";
+
+            $doc = [];
+            if(($tableInfo['rules']['int'] && isset($tableInfo['rules']['int'][$field])) ||
+                ($tableInfo['rules']['number'] && isset($tableInfo['rules']['number'][$field]))
+            ) {
+                $doc[] = "\t/**";
+                $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']))
+            ) {
+                $doc[] = "\t/**";
+                $doc[] = "\t * @var string ". $convertField . $comment;
+                $doc[] = "\t */";
+            } else if ($tableInfo['rules']['timestamp'] && in_array($field, $tableInfo['rules']['timestamp'])) {
+                $doc[] = "\t/**";
+                $doc[] = "\t * @var datetime ". $convertField . $comment;
+                $doc[] = "\t */";
+            } else if($tableInfo['rules']['float'] && isset($tableInfo['rules']['float'][$field])) {
+                $doc[] = "\t/**";
+                $doc[] = "\t * @var float ". $convertField . $comment;
+                $doc[] = "\t */";
+            }
+            if($tableInfo['rules']['default'] && isset($tableInfo['rules']['default'][$field])) {
+                $default = $tableInfo['rules']['default'][$field];
+                $defaults[$convertField] = $default;
+            }
+            if(count($doc) > 0) {
+                $docs[$convertField] = join("\n", $doc);
+            }
+
+            $properties[] = $convertField;
+        }
+        $class = $className != '' ? $this->convertToProperty($className) : $this->convertToProperty($table);
+        $classAndProperty = [];
+        $classAndProperty[] = "<?php";
+        $classAndProperty[] = "namespace ". $this->namespace . ";";
+        $classAndProperty[] = "\n";
+        $classAndProperty[] = "use Qii\Driver\Entity\Inf;";
+        $classAndProperty[] = "use Qii\Driver\Entity\Base;";
+        $classAndProperty[] = "\n";
+        $classAndProperty[] = 'class '. $class . ' extends Base implements Inf{';
+        $dynamicProperty = [];
+        foreach ($properties as $property) {
+            if(isset($docs[$property])) {
+                $classAndProperty[] = $docs[$property];
+            }
+            /*if(isset($defaults[$property])) {
+                $default = $defaults[$property];
+                if($default == 'CURRENT_TIMESTAMP') {
+                    $dynamicProperty[$property] = $default;
+                }
+                $classAndProperty[] = "\t". 'public $'. $property . " = '". $default . "';\n";
+            }else{
+                $classAndProperty[] = "\t". 'public $'. $property . ";";
+            }*/
+            $classAndProperty[] = "\t". 'public $'. $property . ";";
+            if(isset($defaults[$property])) {
+                $dynamicProperty[$property] = $defaults[$property];
+            }
+        }
+        if(count($dynamicProperty)) {
+            $classAndProperty[] = <<<DOC
+    /**
+     * 动态变化的日期,初始化的时候赋值
+     */
+DOC;
+            ;
+            $classAndProperty[] = "\tpublic function __construct(){";
+            $classAndProperty[] = "\n";
+            $classAndProperty[] = "\t}";
+
+            $classAndProperty[] = "\t/**";
+            $classAndProperty[] = "\t * 设置默认值";
+            $classAndProperty[] = "\t */";
+            $classAndProperty[] = "\tpublic function defaultFieldsValue(){";
+            $classAndProperty[] = "\t\t\$defaultValue = [];";
+            foreach ($dynamicProperty as $property => $dynamic) {
+                if($dynamic == 'CURRENT_TIMESTAMP') {
+                    $classAndProperty[] = "\t\t" . '$defaultValue[\''. $property ."'] = date('Y-m-d H:i:s');";
+                }else{
+                    $classAndProperty[] = "\t\t" . '$defaultValue[\''. $property ."'] = '". str_replace("'", "\\'", $dynamic)."';";
+                }
+            }
+            $classAndProperty[] = "\t\treturn \$defaultValue;";
+            $classAndProperty[] = "\t}";
+        }
+        $classAndProperty[] = <<<DOC
+
+\t/**
+\t * 获取表名
+\t */
+\tpublic function getTable(){
+\t\treturn '{$table}';
+\t}
+DOC;
+
+        //生成 table rules
+        $classAndProperty[] = $this->generateRules($tableInfo);
+        $classAndProperty[] = '}';
+        return join("\n", $classAndProperty);
+    }
+
+    public function generateRules($tableInfo){
+        $constantJoin[] = <<<DOC
+
+    /**
+     * 字段验证数据规则
+     */
+    public function rules() { 
+DOC;
+        $constantJoin[] = "\t\t". '$rules = [];';
+        $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];
+                $comments = $tableInfo['comment'];
+                $join = [];
+                switch ($item) {
+                    case 'required':
+                        $join = $this->generateRequired($currentRules, $comments);
+                        break;
+                    case 'int':
+                        $join = $this->generateInt($currentRules, $comments);
+                        break;
+                    case 'minlength':
+                        $join = $this->generateMinLength($currentRules, $comments);
+                        break;
+                    case 'maxlength':
+                        $join = $this->generateMaxLength($currentRules, $comments);
+                        break;
+                    case 'timestamp':
+                    case 'datetime':
+                        $join = $this->generateDatetime($currentRules, $comments);
+                }
+                if(!empty($join)) {
+                    $constantJoin = array_merge($constantJoin, $join);
+                }
+                return $carry($tableInfo, $constantJoin);
+            };
+        }, function($tableInfo, $constantJoin){
+            return $constantJoin;
+        })($tableInfo, $constantJoin);
+        $next[] = "\t\treturn ". '$rules;';
+        $next[] = "\t". '}';
+        $next[] = <<<DOC
+
+    /**
+     * 添加时验证的字段,自行添加
+     */
+    public function validFieldsForAdd(){
+        \$fields = [];
+        return \$this->valid(\$fields);
+    }
+    
+    /**
+     * 验证更新时的字段,自行添加
+     */
+    public function validFieldsForUpdate() {
+        \$fields = [];
+        return \$this->valid(\$fields);
+    }
+DOC;
+
+        return join("\n", $next);
+    }
+
+    /**
+     * required
+     * @param array $rules 规则
+     * @param array $comments field comment
+     * @return array
+     */
+    public function generateRequired($rules, $comments = array()) {
+        $constantJoin = [];
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+        $constantJoin[] = "\t\t". '$rules["required"] = [';
+
+        $join = [];
+        foreach($rules as $key => $value) {
+            $comment = '';
+            if(isset($comments[$value]) && $comments[$value] != "") {
+                $comment = $comments[$value];
+            }
+            $message = ($comment != '' ? $comment :  $value) . "不能为空";
+            $join[] = <<<DOC
+    \t\t["{$this->convertToProperty($value)}", "{$message}"]
+DOC;
+        }
+        $constantJoin[] = join(",\n", $join);
+        $constantJoin[] = "\t\t];";
+        return $constantJoin;
+    }
+    public function generateMaxLength($rules, $comments = array()) {
+        $constantJoin = [];
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+        $constantJoin[] = "\t\t". '$rules["maxLength"] = [';
+        $join = [];
+        foreach($rules as $key => $value) {
+            $comment = '';
+            if(isset($comments[$value]) && $comments[$value] != "") {
+                $comment = $comments[$value];
+            }
+            $message = ($comment != '' ? $comment : $key) . "不能超过 ". $value ."个字符";
+            $join[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$message}", {$value}]
+DOC;
+        }
+        $constantJoin[] = join(",\n", $join);
+        $constantJoin[] = "\t\t];";
+        return $constantJoin;
+    }
+    public function generateMinLength($rules, $comments = array()) {
+        $constantJoin = [];
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+        $constantJoin[] = "\t\t". '$rules["minLength"] = [';
+        $join = [];
+        foreach($rules as $key => $value) {
+            $comment = '';
+            if(isset($comments[$value]) && $comments[$value] != "") {
+                $comment = $comments[$value];
+            }
+            $message = ($comment != '' ? $comment : $key) . "不能少于 ". $value ."个字符";
+            $join[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$message}", {$value}]
+DOC;
+        }
+        $constantJoin[] = join(",\n", $join);
+        $constantJoin[] = "\t\t];";
+        return $constantJoin;
+    }
+
+    /**
+     * datetime 类型
+     * @param array $rules
+     * @param $comments
+     * @return array
+     */
+    public function generateDatetime($rules, $comments = array()) {
+        $constantJoin = [];
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+
+        $constantJoin[] = "\t\t". '$rules["date"] = [';
+
+        $join = [];
+        foreach($rules as $key => $value) {
+            $comment = '';
+            if(isset($comments[$value]) && $comments[$value] != "") {
+                $comment = $comments[$value];
+            }
+            $message = $comment . "格式不正确";
+            $join[] = <<<DOC
+    \t\t["{$this->convertToProperty($value)}", "{$message}"]
+DOC;
+        }
+        $constantJoin[] = join(",\n", $join);
+        $constantJoin[] = "\t\t];";
+        return $constantJoin;
+    }
+    public function generateInt($rules, $comments = array()) {
+        $constantJoin = [];
+
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+        $intProperty['min'][] = "\t\t". '$rules["minNumber"] = [';
+        $intProperty['max'][] = "\t\t". '$rules["maxNumber"] = [';
+        $intProperty['number'][] = "\t\t". '$rules["number"] = [';
+        $joinMin = [];
+        $joinMax = [];
+        $joinNumber = [];
+        foreach ($rules as $key => $val) {
+            $message = isset($comments[$key]) && $comments[$key] != "" ? $comments[$key] : $key;
+            $min = $message . '不能小于'. $val[0];
+            $max = $message . '不能大于'. $val[1];
+            $number = $message . '必须是数字';
+
+            $joinMin[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$min}", {$val[0]}]
+DOC;
+            $joinMax[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$max}", {$val[1]}]
+DOC;
+            $joinNumber[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$number}"]
+DOC;
+
+        }
+        $intProperty['min'][] = join(",\n", $joinMin);
+        $intProperty['min'][] = "\t\t];";
+
+        $intProperty['max'][] = join(",\n", $joinMax);
+        $intProperty['max'][] = "\t\t];";
+
+        $intProperty['number'][] = join(",\n", $joinNumber);
+        $intProperty['number'][] = "\t\t];";
+
+        return array_merge($constantJoin, $intProperty['min'], $intProperty['max'], $intProperty['number']);
+    }
+    /**
+     * 转换为属性,属性会将下划线+小写/大写 转换成大写
+     *
+     * @param $field
+     * @return array|string|string[]|null
+     */
+    public function convertToProperty($field) {
+        $field = $this->ucFirst ? ucfirst($field) : $field;
+        return preg_replace_callback("/\_([a-z]|[A-Z]|[0-9])/", function($match){
+            return ucfirst($match[1]);
+        }, $field);
+    }
+
+    /**
+     * 批量转 property 名称
+     *
+     * @param $fields
+     * @return array|string|string[]|null
+     */
+    public function convertToProperties($fields) {
+        if(!is_array($fields)) {
+            return $this->convertToProperty($fields);
+        }
+        $map = [];
+        foreach ($fields as $val) {
+            $map[] = $this->convertToProperty($val);
+        }
+        return $map;
+    }
+    /**
+     * 将属性转换成字段,属性中的大写会转换成成下划线+小写
+     *
+     * @param $field
+     * @return array|string|string[]|null
+     */
+    public function convertToField($field) {
+        $field = $this->ucFirst ? lcfirst($field) : $field;
+        return preg_replace_callback("/[A-Z]/", function($match){
+            return "_". lcfirst($match[0]);
+        }, $field);
+    }
+
+    /**
+     * 批量转 field
+     * @param array|string $fields
+     * @return array|string|string[]|null
+     */
+    public function convertToFields($fields) {
+        if(!is_array($fields)) {
+            return $this->convertToField($fields);
+        }
+        $map = [];
+        foreach ($fields as $val) {
+            $map[] = $this->convertToField($val);
+        }
+        return $map;
+    }
+}

+ 49 - 0
src/Driver/Entity/Inf.php

@@ -0,0 +1,49 @@
+<?php
+namespace Qii\Driver\Entity;
+/**
+ * 接口
+ */
+interface Inf {
+    public function defaultFieldsValue();
+    /**
+     * 设置主键
+     *
+     * @return mixed
+     */
+    public function primaryKey();
+
+    /**
+     * 设置保存时候唯一性验证
+     *
+     * @return mixed
+     */
+    public function uniqueKey();
+
+    /**
+     * 设置更新时,验证唯一时候,排除的数据
+     *
+     * @return mixed
+     */
+    public function exclude();
+
+    /**
+     * 分页order by
+     *
+     * @return mixed
+     */
+    public function orderBy();
+
+    /**
+     * 保存的时候验证的字段
+     *
+     * @return mixed
+     */
+    public function validFieldsForAdd();
+
+    /**
+     * 保存的时候验证的字段
+     *
+     * @return mixed
+     */
+    public function validFieldsForUpdate();
+}

+ 72 - 0
src/Driver/Entity/Verify.php

@@ -0,0 +1,72 @@
+<?php
+namespace Qii\Driver\Entity;
+
+use Qii\Driver\Response;
+
+class Verify {
+    public $Key;
+    public $Value;
+    public $Valid;
+    public $Message;
+    public $Limit;
+    public $Validator;
+    public function __construct($key, $value, $valid, $message, $limit = null){
+        $this->Key = $key;
+        $this->Value = $value;
+        $this->Valid = $valid;
+        $this->Message = $message;
+        $this->Limit = $limit;
+        $this->Validator = _library('Validate');
+    }
+
+    public function valid() {
+        $verify = [];
+        switch ($this->Valid) {
+            case 'required':
+            case 'email':
+            case 'idcode':
+            case 'number':
+            case 'float':
+            case 'http':
+            case 'qq':
+            case 'postcode':
+            case 'ip':
+            case 'phone':
+            case 'telephone':
+            case 'en':
+            case 'cn':
+            case 'account':
+            case 'date':
+            case 'datetime':
+            case 'safe':
+            case 'password':
+            case 'string':
+            case 'filename':
+                $verify = $this->Validator->verify(
+                    [$this->Key => $this->Value],
+                    [$this->Key => [$this->Valid => true]],
+                    [$this->Key => [$this->Valid => $this->Message]]
+                );
+                break;
+            case 'minlength':
+            case 'maxlength':
+            case 'minnumber':
+            case 'maxnumber':
+            case 'length':
+            case 'rangeof':
+            case 'sets':
+            case 'setsarray':
+            case 'decimal':
+                $verify = $this->Validator->verify(
+                    [$this->Key => $this->Value],
+                    [$this->Key => [$this->Valid => $this->Limit]],
+                    [$this->Key => ['float' => $this->Message]]
+                );
+                break;
+        }
+        if($verify !== true) {
+            return Response::Fail('valid', ['_result' => $verify]);
+        }
+        return Response::Success('valid', ['_result' => true]);
+    }
+}

+ 10 - 8
src/Driver/Response.php

@@ -99,6 +99,8 @@ class Response
 	const FAIL_FOR_UPDATE = 104;
 	//删除失败
 	const FAIL_FOR_REMOVE = 105;
+    //查询失败
+    const FAIL_FOR_SELECT = 108;
 	//类未定义
 	const UNDEFINED_CLASS = 106;
 	//方法未定义
@@ -140,8 +142,8 @@ class Response
 	/**
 	 * 成功
 	 * @param string $operate 操作类型
-	 * @param mix $result 结果
-	 * @return Qii\Driver\Response
+	 * @param array|mixed $result 结果
+	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function Success($operate, $result)
 	{
@@ -151,7 +153,7 @@ class Response
 	/**
 	 * 失败
 	 * @param string $operate 操作类型
-	 * @param mix $result 结果
+	 * @param array $result 结果
 	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function Fail($operate, $result)
@@ -162,7 +164,7 @@ class Response
 	/**
 	 * 记录已存在
 	 * @param string $operate 操作类型
-	 * @param mixed $result 结果
+	 * @param array|mixed $result 结果
 	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function Exist($operate, $result)
@@ -173,7 +175,7 @@ class Response
 	/**
 	 * 记录不存在
 	 * @param string $operate 操作类型
-	 * @param mixed $result 结果
+	 * @param array|mixed $result 结果
 	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function NotExist($operate, $result)
@@ -184,7 +186,7 @@ class Response
 	/**
 	 * 验证失败
 	 * @param string $operate 操作类型
-	 * @param mixed $result 结果
+	 * @param array|mixed $result 结果
 	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function FailValidate($operate, $result)
@@ -270,7 +272,7 @@ class Response
 	 * 方法未定义
 	 * @param string $operate 操作类型
 	 * @param mix $result 结果
-	 * @return Qii\Driver\Response
+	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function UndefinedMethod($operate, $result)
 	{
@@ -281,7 +283,7 @@ class Response
 	 * 类失败
 	 * @param string $operate 操作类型
 	 * @param mix $result 结果
-	 * @return Qii\Driver\Response
+	 * @return Qii\Driver\Response|mixed
 	 */
 	public static function UndefinedClass($operate, $result)
 	{