Entity.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <?php
  2. namespace Qii\Driver\Entity;
  3. use Qii\Driver\Model;
  4. use Qii\Driver\Response;
  5. /**
  6. * 通过数据表生成 entity
  7. * 使用方法:
  8. * use Qii\Driver\Entity\Entity;
  9. * $entity = new Entity();
  10. * $class = $entity->generateProperties($table);
  11. * file_put_contents('entity/'. $table . '.php', $class);
  12. *
  13. * $fields = $entity->generateField('\entity\'. $table);
  14. * print_r($fields);
  15. *
  16. * entity 生成规则:
  17. * 首字母、下划线+字母 -> 大写字母
  18. */
  19. class Entity {
  20. /**
  21. * @var bool $ucFirst 首字母是否大写
  22. */
  23. public $ucFirst = true;
  24. public $namespace = "entity";
  25. public function __construct(){
  26. }
  27. public function db() {
  28. return _loadClass('\Qii\Driver\Model');
  29. }
  30. /**
  31. * 设置 entity 的名字空间
  32. * @param string $namespace 名字空间
  33. * @return void
  34. */
  35. public function setNamespace($namespace) {
  36. $this->namespace = $namespace;
  37. }
  38. /**
  39. * 通过 Entity 获取数据表的字段
  40. *
  41. * @param $class
  42. * @return array
  43. * @throws \ReflectionException
  44. */
  45. public function generateField($class) {
  46. if(!$class) {
  47. throw new \Exception('Class is null');
  48. }
  49. if(class_exists($class, false)) {
  50. throw new \Exception('Class '. $class .' does not exist');
  51. }
  52. $method = new \ReflectionClass($class);
  53. $properties = $method->getProperties();
  54. $fields = [];
  55. foreach($properties as $property) {
  56. if($property->isPublic()) {
  57. $fields[] = $this->convertToField($property->getName());
  58. }
  59. }
  60. return $fields;
  61. }
  62. /**
  63. * 将数据表生成entity
  64. *
  65. * @param string $table 表名
  66. * @return string
  67. * @throws \Exception
  68. */
  69. public function generateProperties($table, $className = '') {
  70. $db = $this->db();
  71. $tableInfo = $db->getTableInfo($table);
  72. if($db->isError()) {
  73. throw new \Exception($db->getError());
  74. }
  75. if(!$tableInfo || empty($tableInfo['fields'])) {
  76. throw new \Exception($table . ' does not have any field');
  77. }
  78. $properties = [];
  79. $docs = [];
  80. $defaults = [];
  81. foreach ($tableInfo['fields'] as $field) {
  82. $comment = $tableInfo['comment'][$field] ?? '';
  83. $convertField = $this->convertToProperty($field);
  84. $comment = strlen($comment) > 0 ? " ". $comment : "";
  85. $doc = [];
  86. if(($tableInfo['rules']['int'] && isset($tableInfo['rules']['int'][$field])) ||
  87. ($tableInfo['rules']['number'] && isset($tableInfo['rules']['number'][$field]))
  88. ) {
  89. $doc[] = "\t/**";
  90. $doc[] = "\t * @var int ". $convertField . $comment;
  91. $doc[] = "\t */";
  92. } else if (($tableInfo['rules']['maxlength'] && isset($tableInfo['rules']['maxlength'][$field]))
  93. || ($tableInfo['rules']['text'] && in_array($field, $tableInfo['rules']['text']))
  94. ) {
  95. $doc[] = "\t/**";
  96. $doc[] = "\t * @var string ". $convertField . $comment;
  97. $doc[] = "\t */";
  98. } else if ($tableInfo['rules']['timestamp'] && in_array($field, $tableInfo['rules']['timestamp'])) {
  99. $doc[] = "\t/**";
  100. $doc[] = "\t * @var datetime ". $convertField . $comment;
  101. $doc[] = "\t */";
  102. } else if($tableInfo['rules']['float'] && isset($tableInfo['rules']['float'][$field])) {
  103. $doc[] = "\t/**";
  104. $doc[] = "\t * @var float ". $convertField . $comment;
  105. $doc[] = "\t */";
  106. }else if($tableInfo['rules']['decimal'] && isset($tableInfo['rules']['decimal'][$field])) {
  107. $doc[] = "\t/**";
  108. $doc[] = "\t * @var decimal ". $convertField . $comment;
  109. $doc[] = "\t */";
  110. }
  111. if($tableInfo['rules']['default'] && isset($tableInfo['rules']['default'][$field])) {
  112. $default = $tableInfo['rules']['default'][$field];
  113. $defaults[$convertField] = $default;
  114. }
  115. if(count($doc) > 0) {
  116. $docs[$convertField] = join("\n", $doc);
  117. }
  118. $properties[] = $convertField;
  119. }
  120. $class = $className != '' ? $this->convertToProperty($className) : $this->convertToProperty($table);
  121. $classAndProperty = [];
  122. $classAndProperty[] = "<?php";
  123. $classAndProperty[] = "namespace ". $this->namespace . ";";
  124. $classAndProperty[] = "\n";
  125. $classAndProperty[] = "use Qii\Driver\Entity\Inf;";
  126. $classAndProperty[] = "use Qii\Driver\Entity\Base;";
  127. $classAndProperty[] = "use Qii\Driver\Response;";
  128. $classAndProperty[] = "\n";
  129. $classAndProperty[] = <<<DOC
  130. /**
  131. * @method \$this setWhereHooker(callable \$func)
  132. * @method \$this setOrderHooker(callable \$func)
  133. * @method \$this setLimitHooker(callable \$func)
  134. * @method \$this setCacheHooker(callable \$func)
  135. * @method \$this setQueryFieldsHooker(callable \$func)
  136. * @method \$this clear()
  137. * @method array properties()
  138. * @method bool exit()
  139. * @method \$this get()
  140. * @method Response info()
  141. * @method Response add()
  142. * @method Response remove()
  143. * @method Response update()
  144. * @method Response updateFields()
  145. * @method Response incr()
  146. * @method Response first()
  147. * @method Response last()
  148. * @method array lists(int \$page, int \$pageSize)
  149. * @method array listAll()
  150. * @method Object rs()
  151. * @method Response valids
  152. * @method array initPages(array \$data, int \$count, int \$page, int \$pageSize)
  153. */
  154. DOC;
  155. $classAndProperty[] = 'class '. $class . ' extends Base implements Inf{';
  156. $dynamicProperty = [];
  157. foreach ($properties as $property) {
  158. if(isset($docs[$property])) {
  159. $classAndProperty[] = $docs[$property];
  160. }
  161. /*if(isset($defaults[$property])) {
  162. $default = $defaults[$property];
  163. if($default == 'CURRENT_TIMESTAMP') {
  164. $dynamicProperty[$property] = $default;
  165. }
  166. $classAndProperty[] = "\t". 'public $'. $property . " = '". $default . "';\n";
  167. }else{
  168. $classAndProperty[] = "\t". 'public $'. $property . ";";
  169. }*/
  170. $classAndProperty[] = "\t". 'public $'. $property . ";";
  171. if(isset($defaults[$property])) {
  172. $dynamicProperty[$property] = $defaults[$property];
  173. }
  174. }
  175. if(count($dynamicProperty)) {
  176. $classAndProperty[] = <<<DOC
  177. /**
  178. * 动态变化的日期,初始化的时候赋值
  179. */
  180. DOC;
  181. $classAndProperty[] = "\tpublic function __construct(){";
  182. $classAndProperty[] = "\n";
  183. $classAndProperty[] = "\t}";
  184. $classAndProperty[] = "\t/**";
  185. $classAndProperty[] = "\t * 设置默认值";
  186. $classAndProperty[] = "\t */";
  187. $classAndProperty[] = "\tpublic function defaultFieldsValue(){";
  188. $classAndProperty[] = "\t\t\$defaultValue = [];";
  189. foreach ($dynamicProperty as $property => $dynamic) {
  190. if($dynamic == 'CURRENT_TIMESTAMP') {
  191. $classAndProperty[] = "\t\t" . '$defaultValue[\''. $property ."'] = date('Y-m-d H:i:s');";
  192. }else{
  193. $classAndProperty[] = "\t\t" . '$defaultValue[\''. $property ."'] = '". str_replace("'", "\\'", $dynamic)."';";
  194. }
  195. }
  196. $classAndProperty[] = "\t\treturn \$defaultValue;";
  197. $classAndProperty[] = "\t}";
  198. }
  199. $classAndProperty[] = <<<DOC
  200. \t/**
  201. \t * 获取表名
  202. \t */
  203. \tpublic function getTable(){
  204. \t\treturn '{$table}';
  205. \t}
  206. DOC;
  207. //生成 table rules
  208. $classAndProperty[] = $this->generateRules($tableInfo);
  209. $classAndProperty[] = '}';
  210. return join("\n", $classAndProperty);
  211. }
  212. public function generateRules($tableInfo){
  213. $constantJoin[] = <<<DOC
  214. /**
  215. * 字段验证数据规则
  216. */
  217. public function rules() {
  218. DOC;
  219. $constantJoin[] = "\t\t". '$rules = [];';
  220. $next = array_reduce(array_reverse(['required', 'int', 'minlength', 'maxlength', 'decimal', 'timestamp', 'datetime']), function($carry, $item){
  221. return function() use ($carry, $item){
  222. $tableInfo = func_get_args()[0];
  223. $constantJoin = func_get_args()[1];
  224. $currentRules = $tableInfo['rules'][$item];
  225. $comments = $tableInfo['comment'];
  226. $join = [];
  227. switch ($item) {
  228. case 'required':
  229. $join = $this->generateRequired($currentRules, $comments);
  230. break;
  231. case 'int':
  232. $join = $this->generateInt($currentRules, $comments);
  233. break;
  234. case 'decimal':
  235. $join = $this->generateDecimal($currentRules, $comments);
  236. break;
  237. case 'minlength':
  238. $join = $this->generateMinLength($currentRules, $comments);
  239. break;
  240. case 'maxlength':
  241. $join = $this->generateMaxLength($currentRules, $comments);
  242. break;
  243. case 'timestamp':
  244. case 'datetime':
  245. $join = $this->generateDatetime($currentRules, $comments);
  246. }
  247. if(!empty($join)) {
  248. $constantJoin = array_merge($constantJoin, $join);
  249. }
  250. return $carry($tableInfo, $constantJoin);
  251. };
  252. }, function($tableInfo, $constantJoin){
  253. return $constantJoin;
  254. })($tableInfo, $constantJoin);
  255. $next[] = "\t\treturn ". '$rules;';
  256. $next[] = "\t". '}';
  257. //主键自动添加
  258. $primaryKey = array();
  259. if(isset($tableInfo['rules']['pri'])) {
  260. $primaryKey = $tableInfo['rules']['pri'];
  261. }
  262. //保存验证的时候使用 required 并去掉pri和default里边的内容
  263. $defaults = array();
  264. if(isset($tableInfo['rules']['default'])) {
  265. $defaults = array_keys($tableInfo['rules']['default']);
  266. }
  267. $validForSave = array();
  268. if(isset($tableInfo['rules']) && isset($tableInfo['rules']['required'])) {
  269. foreach ($tableInfo['rules']['required'] as $field) {
  270. if(!in_array($field, $primaryKey) && !in_array($field, $defaults)) {
  271. $validForSave[] = $this->convertToProperty($field);
  272. }
  273. }
  274. }
  275. $uniKey = array();
  276. if(isset($tableInfo['rules']) && isset($tableInfo['rules']['uniq'])) {
  277. foreach ($tableInfo['rules']['uniq'] as $uniq) {
  278. $uniKey[] = $this->convertToProperties($uniq);
  279. }
  280. }
  281. $next[] = <<<DOC
  282. /**
  283. * unique (unique 如果是 array 则表示 联合唯一,如果是string则是任意唯一,多个单独唯一使用字符串以逗号隔开, 主键除外)
  284. * array(array1, array2) , array1 和array 2单独唯一
  285. * @return mixed
  286. * @throws \Exception
  287. */
  288. public function uniqueKey(){
  289. DOC;
  290. if(count($uniKey) > 0) {
  291. $uniq = array();
  292. foreach ($uniKey as $val) {
  293. $uniq[] = "array('". join("','", $val). "')";
  294. }
  295. $next[] = "\t\treturn array(". join(", ", $uniq). ");";
  296. }else{
  297. $next[] = <<<DOC
  298. return array();
  299. DOC;
  300. }
  301. $next[] = <<<DOC
  302. }
  303. /**
  304. * 主键
  305. *
  306. * @return mixed
  307. * @throws \Exception
  308. */
  309. public function primaryKey(){
  310. DOC;
  311. $exclude = "\t\treturn array();";
  312. if($primaryKey) {
  313. $next[] = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
  314. $exclude = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
  315. }else{
  316. $next[] = <<<DOC
  317. throw new \Exception('请设置主键');
  318. DOC;
  319. }
  320. $next[] = <<<DOC
  321. }
  322. /**
  323. * 更新数据的时候,验证唯一需要排除的值,此处仅支持,单个或联合排除,不支持单个排除
  324. *
  325. * @return array
  326. */
  327. public function exclude(){
  328. DOC;
  329. $next[] = $exclude;
  330. $next[] = <<<DOC
  331. }
  332. /**
  333. * 添加时验证的字段,自行添加
  334. */
  335. public function validFieldsForAdd(){
  336. DOC;
  337. if(count($validForSave) > 0) {
  338. $next[] = "\t\$fields = array('". join("', '", $validForSave)."');";
  339. }else{
  340. $next[] = "\t\$fields = array();";
  341. }
  342. $next[] = <<<DOC
  343. return \$this->valid(\$fields);
  344. }
  345. /**
  346. * 更新时验证的字段,自行添加
  347. */
  348. public function validFieldsForUpdate() {
  349. \$fields = array();
  350. return \$this->valid(\$fields);
  351. }
  352. DOC;
  353. return join("\n", $next);
  354. }
  355. /**
  356. * required
  357. * @param array $rules 规则
  358. * @param array $comments field comment
  359. * @return array
  360. */
  361. public function generateRequired($rules, $comments = array()) {
  362. $constantJoin = [];
  363. if(!$rules || !is_array($rules)) {
  364. return $constantJoin;
  365. }
  366. $constantJoin[] = "\t\t". '$rules["required"] = [';
  367. $join = [];
  368. foreach($rules as $key => $value) {
  369. $comment = '';
  370. if(isset($comments[$value]) && $comments[$value] != "") {
  371. $comment = $comments[$value];
  372. }
  373. $message = ($comment != '' ? $comment : $value) . "不能为空";
  374. $join[] = <<<DOC
  375. \t\t["{$this->convertToProperty($value)}", "{$message}"]
  376. DOC;
  377. }
  378. $constantJoin[] = join(",\n", $join);
  379. $constantJoin[] = "\t\t];";
  380. return $constantJoin;
  381. }
  382. public function generateMaxLength($rules, $comments = array()) {
  383. $constantJoin = [];
  384. if(!$rules || !is_array($rules)) {
  385. return $constantJoin;
  386. }
  387. $constantJoin[] = "\t\t". '$rules["maxLength"] = [';
  388. $join = [];
  389. foreach($rules as $key => $value) {
  390. $comment = '';
  391. if(isset($comments[$value]) && $comments[$value] != "") {
  392. $comment = $comments[$value];
  393. }
  394. $message = ($comment != '' ? $comment : $key) . "不能超过 ". $value ."个字符";
  395. $join[] = <<<DOC
  396. \t\t["{$this->convertToProperty($key)}", "{$message}", {$value}]
  397. DOC;
  398. }
  399. $constantJoin[] = join(",\n", $join);
  400. $constantJoin[] = "\t\t];";
  401. return $constantJoin;
  402. }
  403. public function generateMinLength($rules, $comments = array()) {
  404. $constantJoin = [];
  405. if(!$rules || !is_array($rules)) {
  406. return $constantJoin;
  407. }
  408. $constantJoin[] = "\t\t". '$rules["minLength"] = [';
  409. $join = [];
  410. foreach($rules as $key => $value) {
  411. $comment = '';
  412. if(isset($comments[$value]) && $comments[$value] != "") {
  413. $comment = $comments[$value];
  414. }
  415. $message = ($comment != '' ? $comment : $key) . "不能少于 ". $value ."个字符";
  416. $join[] = <<<DOC
  417. \t\t["{$this->convertToProperty($key)}", "{$message}", {$value}]
  418. DOC;
  419. }
  420. $constantJoin[] = join(",\n", $join);
  421. $constantJoin[] = "\t\t];";
  422. return $constantJoin;
  423. }
  424. /**
  425. * datetime 类型
  426. * @param array $rules
  427. * @param $comments
  428. * @return array
  429. */
  430. public function generateDatetime($rules, $comments = array()) {
  431. $constantJoin = [];
  432. if(!$rules || !is_array($rules)) {
  433. return $constantJoin;
  434. }
  435. $constantJoin[] = "\t\t". '$rules["date"] = [';
  436. $join = [];
  437. foreach($rules as $key => $value) {
  438. $comment = '';
  439. if(isset($comments[$value]) && $comments[$value] != "") {
  440. $comment = $comments[$value];
  441. }
  442. $message = $comment . "格式不正确";
  443. $join[] = <<<DOC
  444. \t\t["{$this->convertToProperty($value)}", "{$message}"]
  445. DOC;
  446. }
  447. $constantJoin[] = join(",\n", $join);
  448. $constantJoin[] = "\t\t];";
  449. return $constantJoin;
  450. }
  451. /**
  452. * int 类型数据
  453. * @param $rules
  454. * @param $comments
  455. * @return array
  456. */
  457. public function generateInt($rules, $comments = array()) {
  458. $constantJoin = [];
  459. if(!$rules || !is_array($rules)) {
  460. return $constantJoin;
  461. }
  462. $intProperty['min'][] = "\t\t". '$rules["minNumber"] = [';
  463. $intProperty['max'][] = "\t\t". '$rules["maxNumber"] = [';
  464. $intProperty['number'][] = "\t\t". '$rules["number"] = [';
  465. $joinMin = [];
  466. $joinMax = [];
  467. $joinNumber = [];
  468. foreach ($rules as $key => $val) {
  469. $message = isset($comments[$key]) && $comments[$key] != "" ? $comments[$key] : $key;
  470. $min = $message . '不能小于'. $val[0];
  471. $max = $message . '不能大于'. $val[1];
  472. $number = $message . '必须是数字';
  473. $joinMin[] = <<<DOC
  474. \t\t["{$this->convertToProperty($key)}", "{$min}", {$val[0]}]
  475. DOC;
  476. $joinMax[] = <<<DOC
  477. \t\t["{$this->convertToProperty($key)}", "{$max}", {$val[1]}]
  478. DOC;
  479. $joinNumber[] = <<<DOC
  480. \t\t["{$this->convertToProperty($key)}", "{$number}"]
  481. DOC;
  482. }
  483. $intProperty['min'][] = join(",\n", $joinMin);
  484. $intProperty['min'][] = "\t\t];";
  485. $intProperty['max'][] = join(",\n", $joinMax);
  486. $intProperty['max'][] = "\t\t];";
  487. $intProperty['number'][] = join(",\n", $joinNumber);
  488. $intProperty['number'][] = "\t\t];";
  489. return array_merge($constantJoin, $intProperty['min'], $intProperty['max'], $intProperty['number']);
  490. }
  491. public function generateDecimal($rules, $comments = array()) {
  492. $constantJoin = [];
  493. if(!$rules || !is_array($rules)) {
  494. return $constantJoin;
  495. }
  496. $constantJoin = [];
  497. $constantJoin[] = "\t\t". '$rules["decimal"] = [';
  498. $joinProperty = [];
  499. foreach ($rules as $key => $val) {
  500. $comment = '';
  501. if(isset($comments[$key]) && $comments[$key] != "") {
  502. $comment = $comments[$key];
  503. }
  504. $message = ($comment != '' ? $comment : $key) . "格式示例: ". str_repeat(6, min(1, (int)$val[0] - (int)$val[1])) . ".". str_repeat(6, (int)$val[1]);
  505. $joinProperty[] = <<<DOC
  506. \t\t["{$this->convertToProperty($key)}", "{$message}", "{$val[0]},{$val[1]}"]
  507. DOC;
  508. }
  509. $constantJoin[] = join(",\n", $joinProperty);
  510. $constantJoin[] = "\t\t];";
  511. return $constantJoin;
  512. }
  513. /**
  514. * 转换为属性,属性会将下划线+小写/大写 转换成大写
  515. *
  516. * @param $field
  517. * @return array|string|string[]|null
  518. */
  519. public function convertToProperty($field) {
  520. $field = $this->ucFirst ? ucfirst($field) : $field;
  521. return preg_replace_callback("/\_([a-z]|[A-Z]|[0-9])/", function($match){
  522. return ucfirst($match[1]);
  523. }, $field);
  524. }
  525. /**
  526. * 批量转 property 名称
  527. *
  528. * @param $fields
  529. * @return array|string|string[]|null
  530. */
  531. public function convertToProperties($fields) {
  532. if(!is_array($fields)) {
  533. return $this->convertToProperty($fields);
  534. }
  535. $map = [];
  536. foreach ($fields as $val) {
  537. $map[] = $this->convertToProperty($val);
  538. }
  539. return $map;
  540. }
  541. /**
  542. * 将属性转换成字段,属性中的大写会转换成成下划线+小写
  543. *
  544. * @param $field
  545. * @return array|string|string[]|null
  546. */
  547. public function convertToField($field) {
  548. $field = $this->ucFirst ? lcfirst($field) : $field;
  549. return preg_replace_callback("/[A-Z]/", function($match){
  550. return "_". lcfirst($match[0]);
  551. }, $field);
  552. }
  553. /**
  554. * 批量转 field
  555. * @param array|string $fields
  556. * @return array|string|string[]|null
  557. */
  558. public function convertToFields($fields) {
  559. if(!is_array($fields)) {
  560. return $this->convertToField($fields);
  561. }
  562. $map = [];
  563. foreach ($fields as $val) {
  564. $map[] = $this->convertToField($val);
  565. }
  566. return $map;
  567. }
  568. }