TraitDatabase.php 13 KB

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