Factories
2 weeks ago
Migrations
2 weeks ago
Seeds
2 weeks ago
DatabaseInspectionCommand.php
2 weeks ago
DbCommand.php
2 weeks ago
DumpCommand.php
2 weeks ago
MonitorCommand.php
2 weeks ago
PruneCommand.php
2 weeks ago
ShowCommand.php
2 weeks ago
TableCommand.php
2 weeks ago
WipeCommand.php
2 weeks ago
TableCommand.php
165 lines
| 1 | <?php |
| 2 | |
| 3 | namespace IAWPSCOPED\Illuminate\Database\Console; |
| 4 | |
| 5 | use IAWPSCOPED\Doctrine\DBAL\Schema\Column; |
| 6 | use IAWPSCOPED\Doctrine\DBAL\Schema\ForeignKeyConstraint; |
| 7 | use IAWPSCOPED\Doctrine\DBAL\Schema\Index; |
| 8 | use IAWPSCOPED\Doctrine\DBAL\Schema\Table; |
| 9 | use IAWPSCOPED\Illuminate\Database\ConnectionResolverInterface; |
| 10 | use IAWPSCOPED\Illuminate\Support\Str; |
| 11 | use IAWPSCOPED\Symfony\Component\Console\Attribute\AsCommand; |
| 12 | /** @internal */ |
| 13 | #[AsCommand(name: 'db:table')] |
| 14 | class TableCommand extends DatabaseInspectionCommand |
| 15 | { |
| 16 | /** |
| 17 | * The name and signature of the console command. |
| 18 | * |
| 19 | * @var string |
| 20 | */ |
| 21 | protected $signature = 'db:table |
| 22 | {table? : The name of the table} |
| 23 | {--database= : The database connection} |
| 24 | {--json : Output the table information as JSON}'; |
| 25 | /** |
| 26 | * The console command description. |
| 27 | * |
| 28 | * @var string |
| 29 | */ |
| 30 | protected $description = 'Display information about the given database table'; |
| 31 | /** |
| 32 | * Execute the console command. |
| 33 | * |
| 34 | * @return int |
| 35 | */ |
| 36 | public function handle(ConnectionResolverInterface $connections) |
| 37 | { |
| 38 | if (!$this->ensureDependenciesExist()) { |
| 39 | return 1; |
| 40 | } |
| 41 | $connection = $connections->connection($this->input->getOption('database')); |
| 42 | $schema = $connection->getDoctrineSchemaManager(); |
| 43 | $this->registerTypeMappings($schema->getDatabasePlatform()); |
| 44 | $table = $this->argument('table') ?: $this->components->choice('Which table would you like to inspect?', \IAWPSCOPED\collect($schema->listTables())->flatMap(fn(Table $table) => [$table->getName()])->toArray()); |
| 45 | if (!$schema->tablesExist([$table])) { |
| 46 | return $this->components->warn("Table [{$table}] doesn't exist."); |
| 47 | } |
| 48 | $table = $schema->listTableDetails($table); |
| 49 | $columns = $this->columns($table); |
| 50 | $indexes = $this->indexes($table); |
| 51 | $foreignKeys = $this->foreignKeys($table); |
| 52 | $data = ['table' => ['name' => $table->getName(), 'columns' => $columns->count(), 'size' => $this->getTableSize($connection, $table->getName())], 'columns' => $columns, 'indexes' => $indexes, 'foreign_keys' => $foreignKeys]; |
| 53 | $this->display($data); |
| 54 | return 0; |
| 55 | } |
| 56 | /** |
| 57 | * Get the information regarding the table's columns. |
| 58 | * |
| 59 | * @param \Doctrine\DBAL\Schema\Table $table |
| 60 | * @return \Illuminate\Support\Collection |
| 61 | */ |
| 62 | protected function columns(Table $table) |
| 63 | { |
| 64 | return \IAWPSCOPED\collect($table->getColumns())->map(fn(Column $column) => ['column' => $column->getName(), 'attributes' => $this->getAttributesForColumn($column), 'default' => $column->getDefault(), 'type' => $column->getType()->getName()]); |
| 65 | } |
| 66 | /** |
| 67 | * Get the attributes for a table column. |
| 68 | * |
| 69 | * @param \Doctrine\DBAL\Schema\Column $column |
| 70 | * @return \Illuminate\Support\Collection |
| 71 | */ |
| 72 | protected function getAttributesForColumn(Column $column) |
| 73 | { |
| 74 | return \IAWPSCOPED\collect([$column->getAutoincrement() ? 'autoincrement' : null, 'type' => $column->getType()->getName(), $column->getUnsigned() ? 'unsigned' : null, !$column->getNotNull() ? 'nullable' : null])->filter(); |
| 75 | } |
| 76 | /** |
| 77 | * Get the information regarding the table's indexes. |
| 78 | * |
| 79 | * @param \Doctrine\DBAL\Schema\Table $table |
| 80 | * @return \Illuminate\Support\Collection |
| 81 | */ |
| 82 | protected function indexes(Table $table) |
| 83 | { |
| 84 | return \IAWPSCOPED\collect($table->getIndexes())->map(fn(Index $index) => ['name' => $index->getName(), 'columns' => \IAWPSCOPED\collect($index->getColumns()), 'attributes' => $this->getAttributesForIndex($index)]); |
| 85 | } |
| 86 | /** |
| 87 | * Get the attributes for a table index. |
| 88 | * |
| 89 | * @param \Doctrine\DBAL\Schema\Index $index |
| 90 | * @return \Illuminate\Support\Collection |
| 91 | */ |
| 92 | protected function getAttributesForIndex(Index $index) |
| 93 | { |
| 94 | return \IAWPSCOPED\collect(['compound' => \count($index->getColumns()) > 1, 'unique' => $index->isUnique(), 'primary' => $index->isPrimary()])->filter()->keys()->map(fn($attribute) => Str::lower($attribute)); |
| 95 | } |
| 96 | /** |
| 97 | * Get the information regarding the table's foreign keys. |
| 98 | * |
| 99 | * @param \Doctrine\DBAL\Schema\Table $table |
| 100 | * @return \Illuminate\Support\Collection |
| 101 | */ |
| 102 | protected function foreignKeys(Table $table) |
| 103 | { |
| 104 | return \IAWPSCOPED\collect($table->getForeignKeys())->map(fn(ForeignKeyConstraint $foreignKey) => ['name' => $foreignKey->getName(), 'local_table' => $table->getName(), 'local_columns' => \IAWPSCOPED\collect($foreignKey->getLocalColumns()), 'foreign_table' => $foreignKey->getForeignTableName(), 'foreign_columns' => \IAWPSCOPED\collect($foreignKey->getForeignColumns()), 'on_update' => Str::lower(rescue(fn() => $foreignKey->getOption('onUpdate'), 'N/A')), 'on_delete' => Str::lower(rescue(fn() => $foreignKey->getOption('onDelete'), 'N/A'))]); |
| 105 | } |
| 106 | /** |
| 107 | * Render the table information. |
| 108 | * |
| 109 | * @param array $data |
| 110 | * @return void |
| 111 | */ |
| 112 | protected function display(array $data) |
| 113 | { |
| 114 | $this->option('json') ? $this->displayJson($data) : $this->displayForCli($data); |
| 115 | } |
| 116 | /** |
| 117 | * Render the table information as JSON. |
| 118 | * |
| 119 | * @param array $data |
| 120 | * @return void |
| 121 | */ |
| 122 | protected function displayJson(array $data) |
| 123 | { |
| 124 | $this->output->writeln(\json_encode($data)); |
| 125 | } |
| 126 | /** |
| 127 | * Render the table information formatted for the CLI. |
| 128 | * |
| 129 | * @param array $data |
| 130 | * @return void |
| 131 | */ |
| 132 | protected function displayForCli(array $data) |
| 133 | { |
| 134 | [$table, $columns, $indexes, $foreignKeys] = [$data['table'], $data['columns'], $data['indexes'], $data['foreign_keys']]; |
| 135 | $this->newLine(); |
| 136 | $this->components->twoColumnDetail('<fg=green;options=bold>' . $table['name'] . '</>'); |
| 137 | $this->components->twoColumnDetail('Columns', $table['columns']); |
| 138 | if ($size = $table['size']) { |
| 139 | $this->components->twoColumnDetail('Size', \number_format($size / 1024 / 1024, 2) . 'MiB'); |
| 140 | } |
| 141 | $this->newLine(); |
| 142 | if ($columns->isNotEmpty()) { |
| 143 | $this->components->twoColumnDetail('<fg=green;options=bold>Column</>', 'Type'); |
| 144 | $columns->each(function ($column) { |
| 145 | $this->components->twoColumnDetail($column['column'] . ' <fg=gray>' . $column['attributes']->implode(', ') . '</>', ($column['default'] ? '<fg=gray>' . $column['default'] . '</> ' : '') . '' . $column['type'] . ''); |
| 146 | }); |
| 147 | $this->newLine(); |
| 148 | } |
| 149 | if ($indexes->isNotEmpty()) { |
| 150 | $this->components->twoColumnDetail('<fg=green;options=bold>Index</>'); |
| 151 | $indexes->each(function ($index) { |
| 152 | $this->components->twoColumnDetail($index['name'] . ' <fg=gray>' . $index['columns']->implode(', ') . '</>', $index['attributes']->implode(', ')); |
| 153 | }); |
| 154 | $this->newLine(); |
| 155 | } |
| 156 | if ($foreignKeys->isNotEmpty()) { |
| 157 | $this->components->twoColumnDetail('<fg=green;options=bold>Foreign Key</>', 'On Update / On Delete'); |
| 158 | $foreignKeys->each(function ($foreignKey) { |
| 159 | $this->components->twoColumnDetail($foreignKey['name'] . ' <fg=gray;options=bold>' . $foreignKey['local_columns']->implode(', ') . ' references ' . $foreignKey['foreign_columns']->implode(', ') . ' on ' . $foreignKey['foreign_table'] . '</>', $foreignKey['on_update'] . ' / ' . $foreignKey['on_delete']); |
| 160 | }); |
| 161 | $this->newLine(); |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 |