MakeModelCommand.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <?php
  2. namespace Webman\Console\Commands;
  3. use Doctrine\Inflector\InflectorFactory;
  4. use support\Db;
  5. use Symfony\Component\Console\Command\Command;
  6. use Symfony\Component\Console\Input\InputInterface;
  7. use Symfony\Component\Console\Output\OutputInterface;
  8. use Symfony\Component\Console\Input\InputOption;
  9. use Symfony\Component\Console\Input\InputArgument;
  10. use Symfony\Component\Console\Question\ConfirmationQuestion;
  11. use Webman\Console\Util;
  12. class MakeModelCommand extends Command
  13. {
  14. protected static $defaultName = 'make:model';
  15. protected static $defaultDescription = 'Make model';
  16. /**
  17. * @return void
  18. */
  19. protected function configure()
  20. {
  21. $this->addArgument('name', InputArgument::REQUIRED, 'Model name');
  22. $this->addArgument('type', InputArgument::OPTIONAL, 'Type');
  23. $this->addOption('connection', 'c', InputOption::VALUE_OPTIONAL, 'Select database connection. ');
  24. }
  25. /**
  26. * @param InputInterface $input
  27. * @param OutputInterface $output
  28. * @return int
  29. */
  30. protected function execute(InputInterface $input, OutputInterface $output): int
  31. {
  32. $name = $input->getArgument('name');
  33. $name = Util::nameToClass($name);
  34. $type = $input->getArgument('type');
  35. $connection = $input->getOption('connection');
  36. $output->writeln("Make model $name");
  37. if (!($pos = strrpos($name, '/'))) {
  38. $name = ucfirst($name);
  39. $model_str = Util::guessPath(app_path(), 'model') ?: 'model';
  40. $file = app_path() . DIRECTORY_SEPARATOR . $model_str . DIRECTORY_SEPARATOR . "$name.php";
  41. $namespace = $model_str === 'Model' ? 'App\Model' : 'app\model';
  42. } else {
  43. $name_str = substr($name, 0, $pos);
  44. if($real_name_str = Util::guessPath(app_path(), $name_str)) {
  45. $name_str = $real_name_str;
  46. } else if ($real_section_name = Util::guessPath(app_path(), strstr($name_str, '/', true))) {
  47. $upper = strtolower($real_section_name[0]) !== $real_section_name[0];
  48. } else if ($real_base_controller = Util::guessPath(app_path(), 'controller')) {
  49. $upper = strtolower($real_base_controller[0]) !== $real_base_controller[0];
  50. }
  51. $upper = $upper ?? strtolower($name_str[0]) !== $name_str[0];
  52. if ($upper && !$real_name_str) {
  53. $name_str = preg_replace_callback('/\/([a-z])/', function ($matches) {
  54. return '/' . strtoupper($matches[1]);
  55. }, ucfirst($name_str));
  56. }
  57. $path = "$name_str/" . ($upper ? 'Model' : 'model');
  58. $name = ucfirst(substr($name, $pos + 1));
  59. $file = app_path() . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . "$name.php";
  60. $namespace = str_replace('/', '\\', ($upper ? 'App/' : 'app/') . $path);
  61. }
  62. if (!$type) {
  63. $database = config('database');
  64. if (isset($database['default']) && strpos($database['default'], 'plugin.') === 0) {
  65. $database = false;
  66. }
  67. $thinkorm = config('thinkorm');
  68. if (isset($thinkorm['default']) && strpos($thinkorm['default'], 'plugin.') === 0) {
  69. $thinkorm = false;
  70. }
  71. $type = !$database && $thinkorm ? 'tp' : 'laravel';
  72. }
  73. if (is_file($file)) {
  74. $helper = $this->getHelper('question');
  75. $question = new ConfirmationQuestion("$file already exists. Do you want to override it? (yes/no)", false);
  76. if (!$helper->ask($input, $output, $question)) {
  77. return Command::SUCCESS;
  78. }
  79. }
  80. if ($type == 'tp') {
  81. $this->createTpModel($name, $namespace, $file, $connection);
  82. } else {
  83. $this->createModel($name, $namespace, $file, $connection);
  84. }
  85. return self::SUCCESS;
  86. }
  87. /**
  88. * @param $class
  89. * @param $namespace
  90. * @param $file
  91. * @param string|null $connection
  92. * @return void
  93. */
  94. protected function createModel($class, $namespace, $file, $connection = null)
  95. {
  96. $path = pathinfo($file, PATHINFO_DIRNAME);
  97. if (!is_dir($path)) {
  98. mkdir($path, 0777, true);
  99. }
  100. $table = Util::classToName($class);
  101. $table_val = 'null';
  102. $pk = 'id';
  103. $properties = '';
  104. $connection = $connection ?: 'mysql';
  105. try {
  106. $prefix = config("database.connections.$connection.prefix") ?? '';
  107. $database = config("database.connections.$connection.database");
  108. $inflector = InflectorFactory::create()->build();
  109. $table_plura = $inflector->pluralize($inflector->tableize($class));
  110. $con = Db::connection($connection);
  111. if ($con->select("show tables like '{$prefix}{$table_plura}'")) {
  112. $table_val = "'$table'";
  113. $table = "{$prefix}{$table_plura}";
  114. } else if ($con->select("show tables like '{$prefix}{$table}'")) {
  115. $table_val = "'$table'";
  116. $table = "{$prefix}{$table}";
  117. }
  118. $tableComment = $con->select('SELECT table_comment FROM information_schema.`TABLES` WHERE table_schema = ? AND table_name = ?', [$database, $table]);
  119. if (!empty($tableComment)) {
  120. $comments = $tableComment[0]->table_comment ?? $tableComment[0]->TABLE_COMMENT;
  121. $properties .= " * {$table} {$comments}" . PHP_EOL;
  122. }
  123. foreach ($con->select("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$table' and table_schema = '$database' ORDER BY ordinal_position") as $item) {
  124. if ($item->COLUMN_KEY === 'PRI') {
  125. $pk = $item->COLUMN_NAME;
  126. $item->COLUMN_COMMENT .= "(主键)";
  127. }
  128. $type = $this->getType($item->DATA_TYPE);
  129. $properties .= " * @property $type \${$item->COLUMN_NAME} {$item->COLUMN_COMMENT}\n";
  130. }
  131. } catch (\Throwable $e) {
  132. echo $e->getMessage() . PHP_EOL;
  133. }
  134. $properties = rtrim($properties) ?: ' *';
  135. $model_content = <<<EOF
  136. <?php
  137. namespace $namespace;
  138. use support\Model;
  139. /**
  140. $properties
  141. */
  142. class $class extends Model
  143. {
  144. /**
  145. * The connection name for the model.
  146. *
  147. * @var string|null
  148. */
  149. protected \$connection = '$connection';
  150. /**
  151. * The table associated with the model.
  152. *
  153. * @var string
  154. */
  155. protected \$table = $table_val;
  156. /**
  157. * The primary key associated with the table.
  158. *
  159. * @var string
  160. */
  161. protected \$primaryKey = '$pk';
  162. /**
  163. * Indicates if the model should be timestamped.
  164. *
  165. * @var bool
  166. */
  167. public \$timestamps = false;
  168. }
  169. EOF;
  170. file_put_contents($file, $model_content);
  171. }
  172. /**
  173. * @param $class
  174. * @param $namespace
  175. * @param $file
  176. * @param string|null $connection
  177. * @return void
  178. */
  179. protected function createTpModel($class, $namespace, $file, $connection = null)
  180. {
  181. $path = pathinfo($file, PATHINFO_DIRNAME);
  182. if (!is_dir($path)) {
  183. mkdir($path, 0777, true);
  184. }
  185. $table = Util::classToName($class);
  186. $table_val = 'null';
  187. $pk = 'id';
  188. $properties = '';
  189. $connection = $connection ?: 'mysql';
  190. try {
  191. $prefix = config("thinkorm.connections.$connection.prefix") ?? '';
  192. $database = config("thinkorm.connections.$connection.database");
  193. $con = \think\facade\Db::connect($connection);
  194. if ($con->query("show tables like '{$prefix}{$table}'")) {
  195. $table = "{$prefix}{$table}";
  196. $table_val = "'$table'";
  197. } else if ($con->query("show tables like '{$prefix}{$table}s'")) {
  198. $table = "{$prefix}{$table}s";
  199. $table_val = "'$table'";
  200. }
  201. $tableComment = $con->query('SELECT table_comment FROM information_schema.`TABLES` WHERE table_schema = ? AND table_name = ?', [$database, $table]);
  202. if (!empty($tableComment)) {
  203. $comments = $tableComment[0]['table_comment'] ?? $tableComment[0]['TABLE_COMMENT'];
  204. $properties .= " * {$table} {$comments}" . PHP_EOL;
  205. }
  206. foreach ($con->query("select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from INFORMATION_SCHEMA.COLUMNS where table_name = '$table' and table_schema = '$database' ORDER BY ordinal_position") as $item) {
  207. if ($item['COLUMN_KEY'] === 'PRI') {
  208. $pk = $item['COLUMN_NAME'];
  209. $item['COLUMN_COMMENT'] .= "(主键)";
  210. }
  211. $type = $this->getType($item['DATA_TYPE']);
  212. $properties .= " * @property $type \${$item['COLUMN_NAME']} {$item['COLUMN_COMMENT']}\n";
  213. }
  214. } catch (\Throwable $e) {
  215. echo $e->getMessage() . PHP_EOL;
  216. }
  217. $properties = rtrim($properties) ?: ' *';
  218. $model_content = <<<EOF
  219. <?php
  220. namespace $namespace;
  221. use think\Model;
  222. /**
  223. $properties
  224. */
  225. class $class extends Model
  226. {
  227. /**
  228. * The connection name for the model.
  229. *
  230. * @var string|null
  231. */
  232. protected \$connection = '$connection';
  233. /**
  234. * The table associated with the model.
  235. *
  236. * @var string
  237. */
  238. protected \$table = $table_val;
  239. /**
  240. * The primary key associated with the table.
  241. *
  242. * @var string
  243. */
  244. protected \$pk = '$pk';
  245. }
  246. EOF;
  247. file_put_contents($file, $model_content);
  248. }
  249. /**
  250. * @param string $type
  251. * @return string
  252. */
  253. protected function getType(string $type)
  254. {
  255. if (strpos($type, 'int') !== false) {
  256. return 'integer';
  257. }
  258. switch ($type) {
  259. case 'varchar':
  260. case 'string':
  261. case 'text':
  262. case 'date':
  263. case 'time':
  264. case 'guid':
  265. case 'datetimetz':
  266. case 'datetime':
  267. case 'decimal':
  268. case 'enum':
  269. return 'string';
  270. case 'boolean':
  271. return 'integer';
  272. case 'float':
  273. return 'float';
  274. default:
  275. return 'mixed';
  276. }
  277. }
  278. }