TraitDatabase.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. namespace Qii\Driver;
  3. trait TraitDatabase
  4. {
  5. /**
  6. * 获取数据库中所有的数据表
  7. * @return array
  8. */
  9. public function getAllDatabases()
  10. {
  11. $sql = "SHOW DATABASES";
  12. $rs = $this->setQuery($sql);
  13. $database = array();
  14. while ($row = $rs->fetch()) {
  15. $database[] = $row['Database'];
  16. }
  17. return $database;
  18. }
  19. /**
  20. * 数据库中是否包含指定库
  21. * @param string $database 数据库名
  22. * @return bool
  23. */
  24. public function hasDatabase($database)
  25. {
  26. if (!$database) return false;
  27. $sql = "SHOW DATABASES LIKE '" . $database . "'";
  28. $rs = $this->setQuery($sql);
  29. while ($row = $rs->fetch()) {
  30. $val = array_values($row);
  31. if (in_array($database, $val)) {
  32. return true;
  33. }
  34. }
  35. return false;
  36. }
  37. /**
  38. * 获取当前数据库中所有的表
  39. * @param null|string $database 数据库
  40. * @return array
  41. */
  42. public function getAllTables($database = null)
  43. {
  44. if ($database) {
  45. $this->setQuery('USE ' . $database);
  46. }
  47. $sql = "SHOW TABLES";
  48. $rs = $this->setQuery($sql);
  49. $tables = array();
  50. while ($row = $rs->fetch()) {
  51. if (is_array($row)) {
  52. foreach ($row AS $val) {
  53. $tables[] = $val;
  54. }
  55. }
  56. }
  57. return $tables;
  58. }
  59. /**
  60. * 获取表的索引
  61. *
  62. * @param string $table 表名
  63. * @param string $database 数据库名称
  64. * @return array
  65. */
  66. public function getTableIndex($table, $database = null) {
  67. if (!$database) $database = $this->currentDB;
  68. $sql = 'SHOW INDEX FROM '. $database .'.'. $table;
  69. $rs = $this->setQuery($sql);
  70. $index = array();
  71. //Non_unique 0 是唯一 或主键 (0 + Key_name=(PRIMARY)) 1 是索引
  72. while ($row = $rs->fetch()) {
  73. if($row['Key_name'] == 'PRIMARY') {
  74. $index['PRIMARY'][] = $row['Column_name'];
  75. continue;
  76. }
  77. if($row['Non_unique'] == '0') {
  78. $index['UNIQUE'][$row['Key_name']][] = $row['Column_name'];
  79. continue;
  80. }
  81. $index['INDEX'][$row['Key_name']][] = $row['Column_name'];
  82. }
  83. return $index;
  84. }
  85. /**
  86. * 获取表的备注
  87. *
  88. * @param string $table 表名
  89. * @param string $database 数据库名称
  90. * @return array|mixed
  91. */
  92. public function getTableComment($table = '', $database = null) {
  93. if (!$database) $database = $this->currentDB;
  94. $sql = "
  95. SELECT TABLE_NAME AS table_name, TABLE_COMMENT AS comment
  96. FROM INFORMATION_SCHEMA.TABLES
  97. WHERE TABLE_SCHEMA = '{$database}'";
  98. if($table != '') {
  99. $sql .= " AND TABLE_NAME = '{$table}'";
  100. }
  101. $rs = $this->setQuery($sql);
  102. $comment = array();
  103. while ($row = $rs->fetch()) {
  104. $comment[$row['table_name']] = $row['comment'];
  105. }
  106. if($table != '' && isset($comment[$table])) {
  107. return $comment[$table];
  108. }
  109. return $comment;
  110. }
  111. /**
  112. * 获取指定数据表的所有字段
  113. * @param string $table 表名
  114. * @param string $database 数据库名
  115. * @param string $autoIncr 自动增长的序号
  116. * @return array
  117. */
  118. public function getTableInfo($table, $database = null, $autoIncr = null)
  119. {
  120. static $tableInfo;
  121. if(isset($tableInfo[$table])) {
  122. return $tableInfo[$table];
  123. }
  124. if (!$database) $database = $this->currentDB;
  125. $sql = "SELECT * from information_schema.COLUMNS where table_name = '" . $table . "' and table_schema = '" . $database . "' ORDER BY ORDINAL_POSITION ASC";
  126. $data = array(
  127. 'table' => $table,
  128. 'fields' => array(),
  129. 'fields_type' => array(),
  130. 'rules' => array(
  131. 'pri' => array(),
  132. 'required' => array()
  133. )
  134. );
  135. $rs = $this->setQuery($sql);
  136. $maxRange = [
  137. 'bigint' => [-1 * pow(2, 63), pow(2, 63)-1],
  138. 'int' => [-1 * pow(2, 31), pow(2, 31)-1],
  139. 'mediumint' => [-8388608, 8388607],
  140. 'smallint' => [-1 * pow(2, 15), pow(2, 15)-1],
  141. 'tinyint' => [-128, 127],
  142. 'integer' => [-1 * pow(2, 31), pow(2, 31)-1]
  143. ];
  144. $data['type'] = [];
  145. while ($row = $rs->fetch()) {
  146. $data['fields'][] = $row['COLUMN_NAME'];
  147. $data['type'][$row['DATA_TYPE']][] = $row['COLUMN_NAME'];
  148. $fieldsType = array();
  149. $fieldsType['name'] = $row['COLUMN_NAME'];
  150. $fieldsType['type'] = strtolower($row['DATA_TYPE']);
  151. $fieldsType['is_pri'] = false;
  152. $fieldsType['is_uni'] = false;
  153. $fieldsType['is_null'] = true;
  154. $fieldsType['is_number'] = false;
  155. $fieldsType['is_float'] = false;
  156. $fieldsType['is_decimal'] = false;
  157. $fieldsType['is_timestamp'] = false;
  158. $fieldsType['is_set'] = false;
  159. $fieldsType['default'] = false;
  160. $fieldsType['comment'] = '';
  161. if ($row['EXTRA']) {
  162. $data['rules']['extra'][$row['EXTRA']][] = $row['COLUMN_NAME'];
  163. $fieldsType['extra'] = $row['EXTRA'];
  164. }
  165. if ($row['COLUMN_KEY'] == 'PRI') {
  166. $data['rules']['pri'][] = $row['COLUMN_NAME'];
  167. $data['rules']['required'][] = $row['COLUMN_NAME'];
  168. $fieldsType['is_pri'] = true;
  169. } else if($row['COLUMN_KEY'] == 'UNI'){
  170. //$data['rules']['uni'][] = $row['COLUMN_NAME'];
  171. $data['rules']['required'][] = $row['COLUMN_NAME'];
  172. $fieldsType['is_uni'] = true;
  173. }else if ($row['IS_NULLABLE'] == 'NO') {
  174. $data['rules']['required'][] = $row['COLUMN_NAME'];
  175. $fieldsType['is_null'] = false;
  176. }
  177. if (in_array($row['DATA_TYPE'], array('varchar', 'char'))) {
  178. $data['rules']['maxlength'][$row['COLUMN_NAME']] = $row['CHARACTER_MAXIMUM_LENGTH'];
  179. $fieldsType['maxlength'] = $row['CHARACTER_MAXIMUM_LENGTH'];
  180. }
  181. if (in_array($row['DATA_TYPE'], array('mediumtext', 'tinytext', 'text', 'longtext'))) {
  182. $data['rules']['text'][] = $row['COLUMN_NAME'];
  183. }
  184. if (in_array($row['DATA_TYPE'], array('bigint', 'int', 'mediumint', 'smallint', 'tinyint', 'integer'))) {
  185. preg_match('/[\d]{0,}/', $row['COLUMN_TYPE'], $matches);
  186. if(!empty($matches)) {
  187. $data['rules']['int'][$row['COLUMN_NAME']] = isset($matches[0]) && $matches[0] ? $matches[0] : $maxRange[$row['DATA_TYPE']];
  188. $data['rules']['number'][] = $row['COLUMN_NAME'];
  189. $fieldsType['is_number'] = true;
  190. }
  191. }
  192. if (in_array($row['DATA_TYPE'], array('float', 'double'))) {
  193. $data['rules']['float'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
  194. $fieldsType['is_float'] = true;
  195. }
  196. if (in_array($row['DATA_TYPE'], array('decimal'))) {
  197. $data['rules']['decimal'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
  198. $fieldsType['is_decimal'] = true;
  199. }
  200. if (in_array($row['DATA_TYPE'], array('timestamp', 'datetime'))) {
  201. $data['rules']['timestamp'][] = $row['COLUMN_NAME'];
  202. $fieldsType['is_timestamp'] = true;
  203. }
  204. if (in_array($row['DATA_TYPE'], array('enum', 'set'))) {
  205. $data['rules']['sets'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
  206. $fieldsType['is_set'] = true;
  207. }
  208. if (isset($row['COLUMN_DEFAULT'])) {
  209. $data['rules']['default'][$row['COLUMN_NAME']] = $row['COLUMN_DEFAULT'];
  210. $fieldsType['default'] = $row['COLUMN_DEFAULT'];
  211. }
  212. if(isset($row['COLUMN_COMMENT'])) {
  213. $data['comment'][$row['COLUMN_NAME']] = $row['COLUMN_COMMENT'];
  214. $fieldsType['comment'] = $row['COLUMN_COMMENT'];
  215. }
  216. $data['fields_type'][$row['COLUMN_NAME']] = $fieldsType;
  217. }
  218. $sql = "SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE table_name = '" . $table . "'";
  219. $rs = $this->setQuery($sql);
  220. $data['sql'] = $this->getTableSQL($table, $database, $autoIncr);
  221. preg_match_all("/UNIQUE\sKEY\s`(.*?)`\s\(`(.*?)`\)/", $data['sql'], $matches);
  222. $uniqKeys = [];
  223. if(count($matches) == 3) {
  224. foreach ($matches[2] as $key => $match) {
  225. //$uniqKeys[$matches[1][$key]] = explode("`,`", $match);
  226. $arr = explode("`,`", $match);
  227. if(count($arr) > 0) $uniqKeys[] = join(',', $arr);
  228. }
  229. }
  230. if(count($uniqKeys) > 0) {
  231. $data['rules']['uniq'] = $uniqKeys;
  232. }
  233. $tableInfo[$table] = $data;
  234. return $data;
  235. }
  236. /**
  237. * 从括号中获取指定的值
  238. * @param string $str 需要获取的内容
  239. * @return array
  240. */
  241. public function getValueFromBrackets($str)
  242. {
  243. preg_match("/(?:\()(.*)(?:\))/i", $str, $matches);
  244. if(!$matches) {
  245. return $str;
  246. }
  247. $str = $matches[1];
  248. $a = explode(",", $str);
  249. for ($i = 0; $i < count($a); $i++) {
  250. $this->removeQuote($a[$i]);//从字符串中去除单引号
  251. }
  252. return $a;
  253. }
  254. /**
  255. * 去除双引号
  256. * @param string $str 去除双引号
  257. */
  258. public function removeQuote(&$str)
  259. {
  260. if (preg_match("/^\'/", $str)) {
  261. $str = substr($str, 1, strlen($str) - 1);
  262. }
  263. if (preg_match("/\'$/", $str)) {
  264. $str = substr($str, 0, strlen($str) - 1);
  265. }
  266. return $str;
  267. }
  268. /**
  269. * 查询数据库中是否有指定的表
  270. * @param string $tableName 表名
  271. * @param null|string $database 数据库
  272. * @return bool
  273. */
  274. public function hasTable($tableName, $database = null)
  275. {
  276. if ($database) {
  277. $this->setQuery('USE ' . $database);
  278. }
  279. $sql = "SHOW TABLES LIKE '" . $tableName . "'";
  280. $rs = $this->setQuery($sql);
  281. $tables = array();
  282. while ($row = $this->fetch($rs)) {
  283. if (is_array($row)) {
  284. foreach ($row AS $val) {
  285. if ($val == $tableName) return true;
  286. }
  287. }
  288. }
  289. return false;
  290. }
  291. /**
  292. * 获取创建表的SQL语句
  293. * @param string $tableName 表名
  294. * @param null|string $database 数据库
  295. * @param int|null $autoIncr 自增长的值,null的话就不使用
  296. * @return string
  297. */
  298. public function getTableSQL($tableName, $database = null, $autoIncr = null)
  299. {
  300. if ($database) {
  301. $this->setQuery('USE ' . $database);
  302. }
  303. $row = $this->getRow("SHOW CREATE TABLE `" . $tableName . "`");
  304. if (!$row) {
  305. throw new \Exception('数据表不存在', __LINE__);
  306. }
  307. $sql = $row['Create Table'];
  308. if ($autoIncr === null) {
  309. return $sql;
  310. }
  311. return preg_replace("/AUTO_INCREMENT=[\d]{1,}/", "AUTO_INCREMENT=" . intval($autoIncr), $sql);
  312. }
  313. /**
  314. * 通过数据表名称获取规则
  315. * @param string $table 表名
  316. * @param string $database 数据库名
  317. */
  318. public function buildRulesForTable($table, $database = null)
  319. {
  320. if (!$database) $database = $this->currentDB;
  321. $tableInfo = $this->getTableInfo($table, $database);
  322. $rules = new \Qii\Base\Rules();
  323. $rules->addFields($tableInfo['fields']);
  324. if ($tableInfo['rules']['required']) {
  325. $rules->addForceValidKey($tableInfo['rules']['required']);
  326. }
  327. if (isset($tableInfo['rules']['number'])) {
  328. foreach ($tableInfo['rules']['number'] as $key => $value) {
  329. $rules->addRules($value, 'number', true, $value . '字段必须是数字');
  330. }
  331. }
  332. if (isset($tableInfo['rules']['maxlength'])) {
  333. foreach ($tableInfo['rules']['maxlength'] as $key => $value) {
  334. $rules->addRules($key, 'maxlength', $value, $key . '字段内容长度不能大于' . $value . '个字符');
  335. }
  336. }
  337. if (isset($tableInfo['rules']['timestamp'])) {
  338. foreach ($tableInfo['rules']['timestamp'] as $key => $value) {
  339. $rules->addRules($value, 'datetime', true, $value . '字段必须为日期格式');
  340. }
  341. }
  342. return $rules;
  343. }
  344. /**
  345. * 获取表的名称
  346. * @param String $table
  347. * @return String
  348. */
  349. public function getTable($table)
  350. {
  351. //去掉表名及数据库名的`符号,重新添加,避免重复
  352. $table = str_replace("`", '', $table);
  353. list($database, $tableName) = array_pad(explode('.', $table), 2, '');
  354. if ($tableName) {
  355. return "`{$database}`.`{$tableName}`";
  356. }
  357. return '`'. $table .'`';
  358. }
  359. }