TableCommand.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. <?php
  2. namespace Illuminate\Database\Console;
  3. use Illuminate\Database\ConnectionResolverInterface;
  4. use Illuminate\Database\Schema\Builder;
  5. use Illuminate\Support\Arr;
  6. use Illuminate\Support\Number;
  7. use Symfony\Component\Console\Attribute\AsCommand;
  8. use function Laravel\Prompts\select;
  9. #[AsCommand(name: 'db:table')]
  10. class TableCommand extends DatabaseInspectionCommand
  11. {
  12. /**
  13. * The name and signature of the console command.
  14. *
  15. * @var string
  16. */
  17. protected $signature = 'db:table
  18. {table? : The name of the table}
  19. {--database= : The database connection}
  20. {--json : Output the table information as JSON}';
  21. /**
  22. * The console command description.
  23. *
  24. * @var string
  25. */
  26. protected $description = 'Display information about the given database table';
  27. /**
  28. * Execute the console command.
  29. *
  30. * @return int
  31. */
  32. public function handle(ConnectionResolverInterface $connections)
  33. {
  34. $connection = $connections->connection($this->input->getOption('database'));
  35. $schema = $connection->getSchemaBuilder();
  36. $tables = $schema->getTables();
  37. $tableName = $this->argument('table') ?: select(
  38. 'Which table would you like to inspect?',
  39. array_column($tables, 'name')
  40. );
  41. $table = Arr::first($tables, fn ($table) => $table['name'] === $tableName);
  42. if (! $table) {
  43. $this->components->warn("Table [{$tableName}] doesn't exist.");
  44. return 1;
  45. }
  46. $tableName = $this->withoutTablePrefix($connection, $table['name']);
  47. $columns = $this->columns($schema, $tableName);
  48. $indexes = $this->indexes($schema, $tableName);
  49. $foreignKeys = $this->foreignKeys($schema, $tableName);
  50. $data = [
  51. 'table' => [
  52. 'name' => $table['name'],
  53. 'columns' => count($columns),
  54. 'size' => $table['size'],
  55. ],
  56. 'columns' => $columns,
  57. 'indexes' => $indexes,
  58. 'foreign_keys' => $foreignKeys,
  59. ];
  60. $this->display($data);
  61. return 0;
  62. }
  63. /**
  64. * Get the information regarding the table's columns.
  65. *
  66. * @param \Illuminate\Database\Schema\Builder $schema
  67. * @param string $table
  68. * @return \Illuminate\Support\Collection
  69. */
  70. protected function columns(Builder $schema, string $table)
  71. {
  72. return collect($schema->getColumns($table))->map(fn ($column) => [
  73. 'column' => $column['name'],
  74. 'attributes' => $this->getAttributesForColumn($column),
  75. 'default' => $column['default'],
  76. 'type' => $column['type'],
  77. ]);
  78. }
  79. /**
  80. * Get the attributes for a table column.
  81. *
  82. * @param array $column
  83. * @return \Illuminate\Support\Collection
  84. */
  85. protected function getAttributesForColumn($column)
  86. {
  87. return collect([
  88. $column['type_name'],
  89. $column['auto_increment'] ? 'autoincrement' : null,
  90. $column['nullable'] ? 'nullable' : null,
  91. $column['collation'],
  92. ])->filter();
  93. }
  94. /**
  95. * Get the information regarding the table's indexes.
  96. *
  97. * @param \Illuminate\Database\Schema\Builder $schema
  98. * @param string $table
  99. * @return \Illuminate\Support\Collection
  100. */
  101. protected function indexes(Builder $schema, string $table)
  102. {
  103. return collect($schema->getIndexes($table))->map(fn ($index) => [
  104. 'name' => $index['name'],
  105. 'columns' => collect($index['columns']),
  106. 'attributes' => $this->getAttributesForIndex($index),
  107. ]);
  108. }
  109. /**
  110. * Get the attributes for a table index.
  111. *
  112. * @param array $index
  113. * @return \Illuminate\Support\Collection
  114. */
  115. protected function getAttributesForIndex($index)
  116. {
  117. return collect([
  118. $index['type'],
  119. count($index['columns']) > 1 ? 'compound' : null,
  120. $index['unique'] && ! $index['primary'] ? 'unique' : null,
  121. $index['primary'] ? 'primary' : null,
  122. ])->filter();
  123. }
  124. /**
  125. * Get the information regarding the table's foreign keys.
  126. *
  127. * @param \Illuminate\Database\Schema\Builder $schema
  128. * @param string $table
  129. * @return \Illuminate\Support\Collection
  130. */
  131. protected function foreignKeys(Builder $schema, string $table)
  132. {
  133. return collect($schema->getForeignKeys($table))->map(fn ($foreignKey) => [
  134. 'name' => $foreignKey['name'],
  135. 'columns' => collect($foreignKey['columns']),
  136. 'foreign_schema' => $foreignKey['foreign_schema'],
  137. 'foreign_table' => $foreignKey['foreign_table'],
  138. 'foreign_columns' => collect($foreignKey['foreign_columns']),
  139. 'on_update' => $foreignKey['on_update'],
  140. 'on_delete' => $foreignKey['on_delete'],
  141. ]);
  142. }
  143. /**
  144. * Render the table information.
  145. *
  146. * @param array $data
  147. * @return void
  148. */
  149. protected function display(array $data)
  150. {
  151. $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data);
  152. }
  153. /**
  154. * Render the table information as JSON.
  155. *
  156. * @param array $data
  157. * @return void
  158. */
  159. protected function displayJson(array $data)
  160. {
  161. $this->output->writeln(json_encode($data));
  162. }
  163. /**
  164. * Render the table information formatted for the CLI.
  165. *
  166. * @param array $data
  167. * @return void
  168. */
  169. protected function displayForCli(array $data)
  170. {
  171. [$table, $columns, $indexes, $foreignKeys] = [
  172. $data['table'], $data['columns'], $data['indexes'], $data['foreign_keys'],
  173. ];
  174. $this->newLine();
  175. $this->components->twoColumnDetail('<fg=green;options=bold>'.$table['name'].'</>');
  176. $this->components->twoColumnDetail('Columns', $table['columns']);
  177. if ($size = $table['size']) {
  178. $this->components->twoColumnDetail('Size', Number::fileSize($size, 2));
  179. }
  180. $this->newLine();
  181. if ($columns->isNotEmpty()) {
  182. $this->components->twoColumnDetail('<fg=green;options=bold>Column</>', 'Type');
  183. $columns->each(function ($column) {
  184. $this->components->twoColumnDetail(
  185. $column['column'].' <fg=gray>'.$column['attributes']->implode(', ').'</>',
  186. (! is_null($column['default']) ? '<fg=gray>'.$column['default'].'</> ' : '').$column['type']
  187. );
  188. });
  189. $this->newLine();
  190. }
  191. if ($indexes->isNotEmpty()) {
  192. $this->components->twoColumnDetail('<fg=green;options=bold>Index</>');
  193. $indexes->each(function ($index) {
  194. $this->components->twoColumnDetail(
  195. $index['name'].' <fg=gray>'.$index['columns']->implode(', ').'</>',
  196. $index['attributes']->implode(', ')
  197. );
  198. });
  199. $this->newLine();
  200. }
  201. if ($foreignKeys->isNotEmpty()) {
  202. $this->components->twoColumnDetail('<fg=green;options=bold>Foreign Key</>', 'On Update / On Delete');
  203. $foreignKeys->each(function ($foreignKey) {
  204. $this->components->twoColumnDetail(
  205. $foreignKey['name'].' <fg=gray;options=bold>'.$foreignKey['columns']->implode(', ').' references '.$foreignKey['foreign_columns']->implode(', ').' on '.$foreignKey['foreign_table'].'</>',
  206. $foreignKey['on_update'].' / '.$foreignKey['on_delete'],
  207. );
  208. });
  209. $this->newLine();
  210. }
  211. }
  212. }