| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- <?php
- declare(strict_types=1);
- namespace SixShop\MakerBundle\Generator;
- use Symfony\Component\Console\Style\SymfonyStyle;
- use Phinx\Util\Util;
- class MigrationGenerator
- {
- private string $migrationsPath;
- private array $fieldTypes = [
- 'integer' => 'integer',
- 'biginteger' => 'bigInteger',
- 'string' => 'string',
- 'text' => 'text',
- 'boolean' => 'boolean',
- 'datetime' => 'datetime',
- 'timestamp' => 'timestamp',
- 'date' => 'date',
- 'time' => 'time',
- 'decimal' => 'decimal',
- 'float' => 'float',
- 'binary' => 'binary',
- 'json' => 'json'
- ];
- public function __construct(?string $migrationsPath = null)
- {
- $this->migrationsPath = $migrationsPath ?: getcwd() . '/database/migrations';
- }
- /**
- * Generate migration file based on table definition
- */
- public function generateMigration(string $tableName, array $fields = [], string $action = 'create', ?SymfonyStyle $io = null): bool
- {
- try {
- // Ensure migrations directory exists
- if (!is_dir($this->migrationsPath)) {
- if (!mkdir($this->migrationsPath, 0755, true)) {
- if ($io) {
- $io->error("无法创建迁移目录: {$this->migrationsPath}");
- }
- return false;
- }
- }
- // Generate migration name and class name
- $migrationName = $this->generateMigrationName($tableName, $action);
- $className = $this->generateClassName($migrationName);
- // Generate timestamp
- $timestamp = date('YmdHis');
- $filename = $timestamp . '_' . $migrationName . '.php';
- $filePath = $this->migrationsPath . '/' . $filename;
- // Generate migration content
- $content = $this->generateMigrationContent($className, $tableName, $fields, $action);
- // Write migration file
- if (file_put_contents($filePath, $content) !== false) {
- if ($io) {
- $io->success("Migration created: {$filename}");
- }
- return true;
- } else {
- if ($io) {
- $io->error("无法写入迁移文件: {$filename}");
- }
- return false;
- }
- } catch (\Exception $e) {
- if ($io) {
- $io->error('生成迁移文件时发生错误: ' . $e->getMessage());
- }
- return false;
- }
- }
- /**
- * Generate migration name
- */
- private function generateMigrationName(string $tableName, string $action): string
- {
- switch ($action) {
- case 'create':
- return 'create_' . $tableName . '_table';
- case 'add_column':
- return 'add_columns_to_' . $tableName . '_table';
- case 'drop_column':
- return 'drop_columns_from_' . $tableName . '_table';
- case 'modify':
- return 'modify_' . $tableName . '_table';
- default:
- return 'update_' . $tableName . '_table';
- }
- }
- /**
- * Generate migration class name
- */
- private function generateClassName(string $migrationName): string
- {
- return Util::mapFileNameToClassName($migrationName);
- }
- /**
- * Generate migration file content
- */
- private function generateMigrationContent(string $className, string $tableName, array $fields, string $action): string
- {
- $content = "<?php\n";
- $content .= "declare(strict_types=1);\n\n";
- $content .= "use Phinx\\Migration\\AbstractMigration;\n\n";
- $content .= "final class {$className} extends AbstractMigration\n";
- $content .= "{\n";
- // Generate change method
- $content .= " /**\n";
- $content .= " * Change Method.\n";
- $content .= " *\n";
- $content .= " * Write your reversible migrations using this method.\n";
- $content .= " *\n";
- $content .= " * More information on writing migrations is available here:\n";
- $content .= " * https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method\n";
- $content .= " *\n";
- $content .= " * Remember to call \"create()\" or \"update()\" and NOT \"save()\" when working\n";
- $content .= " * with the Table class.\n";
- $content .= " */\n";
- $content .= " public function change(): void\n";
- $content .= " {\n";
- switch ($action) {
- case 'create':
- $content .= $this->generateCreateTableContent($tableName, $fields);
- break;
- case 'add_column':
- $content .= $this->generateAddColumnContent($tableName, $fields);
- break;
- case 'drop_column':
- $content .= $this->generateDropColumnContent($tableName, $fields);
- break;
- default:
- $content .= $this->generateModifyTableContent($tableName, $fields);
- break;
- }
- $content .= " }\n";
- $content .= "}\n";
- return $content;
- }
- /**
- * Generate create table content
- */
- private function generateCreateTableContent(string $tableName, array $fields): string
- {
- $content = " \$table = \$this->table('{$tableName}');\n";
- if (!empty($fields)) {
- foreach ($fields as $field) {
- $fieldName = $field['name'];
- $fieldType = $this->fieldTypes[$field['type']] ?? 'string';
- $options = $this->buildFieldOptions($field);
- $content .= " \$table->addColumn('{$fieldName}', '{$fieldType}'";
- if (!empty($options)) {
- $content .= ", " . $this->arrayToString($options);
- }
- $content .= ");\n";
- }
- }
- // Add indexes if specified
- foreach ($fields as $field) {
- if (!empty($field['index'])) {
- $content .= " \$table->addIndex(['{$field['name']}']);\n";
- }
- if (!empty($field['unique'])) {
- $content .= " \$table->addIndex(['{$field['name']}'], ['unique' => true]);\n";
- }
- }
- $content .= " \$table->create();\n";
- return $content;
- }
- /**
- * Generate add column content
- */
- private function generateAddColumnContent(string $tableName, array $fields): string
- {
- $content = " \$table = \$this->table('{$tableName}');\n";
- foreach ($fields as $field) {
- $fieldName = $field['name'];
- $fieldType = $this->fieldTypes[$field['type']] ?? 'string';
- $options = $this->buildFieldOptions($field);
- $content .= " \$table->addColumn('{$fieldName}', '{$fieldType}'";
- if (!empty($options)) {
- $content .= ", " . $this->arrayToString($options);
- }
- $content .= ");\n";
- }
- $content .= " \$table->update();\n";
- return $content;
- }
- /**
- * Generate drop column content
- */
- private function generateDropColumnContent(string $tableName, array $fields): string
- {
- $content = " \$table = \$this->table('{$tableName}');\n";
- foreach ($fields as $field) {
- $fieldName = is_array($field) ? $field['name'] : $field;
- $content .= " \$table->removeColumn('{$fieldName}');\n";
- }
- $content .= " \$table->update();\n";
- return $content;
- }
- /**
- * Generate modify table content
- */
- private function generateModifyTableContent(string $tableName, array $fields): string
- {
- $content = " \$table = \$this->table('{$tableName}');\n";
- $content .= " // Add your table modifications here\n";
- $content .= " \$table->update();\n";
- return $content;
- }
- /**
- * Build field options array
- */
- private function buildFieldOptions(array $field): array
- {
- $options = [];
- if (isset($field['length'])) {
- $options['limit'] = (int)$field['length'];
- }
- if (isset($field['null'])) {
- $options['null'] = (bool)$field['null'];
- } else {
- $options['null'] = true; // Default to nullable
- }
- if (isset($field['default'])) {
- $options['default'] = $field['default'];
- }
- if (isset($field['comment'])) {
- $options['comment'] = $field['comment'];
- }
- if (isset($field['after'])) {
- $options['after'] = $field['after'];
- }
- // Handle precision and scale for decimal fields
- if ($field['type'] === 'decimal') {
- if (isset($field['precision'])) {
- $options['precision'] = (int)$field['precision'];
- }
- if (isset($field['scale'])) {
- $options['scale'] = (int)$field['scale'];
- }
- }
- return $options;
- }
- /**
- * Convert array to string representation
- */
- private function arrayToString(array $array): string
- {
- $parts = [];
- foreach ($array as $key => $value) {
- if (is_string($value)) {
- $parts[] = "'{$key}' => '{$value}'";
- } elseif (is_bool($value)) {
- $parts[] = "'{$key}' => " . ($value ? 'true' : 'false');
- } elseif (is_null($value)) {
- $parts[] = "'{$key}' => null";
- } else {
- $parts[] = "'{$key}' => {$value}";
- }
- }
- return '[' . implode(', ', $parts) . ']';
- }
- /**
- * Get supported field types
- */
- public function getSupportedFieldTypes(): array
- {
- return array_keys($this->fieldTypes);
- }
- /**
- * Set migrations path
- */
- public function setMigrationsPath(string $path): void
- {
- $this->migrationsPath = $path;
- }
- /**
- * Get migrations path
- */
- public function getMigrationsPath(): string
- {
- return $this->migrationsPath;
- }
- }
|