ソースを参照

refactor: 统一代码风格

runphp 2 週間 前
コミット
ff07d39510

+ 33 - 32
src/Generator/ComposerGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -22,7 +23,7 @@ class ComposerGenerator
         try {
             // 尝试读取现有的composer.json文件以获取默认值
             $existingInfo = $this->readExistingComposerInfo($targetPath, $io);
-            
+
             // 如果找到了完整的现有信息,询问用户是否直接使用
             if ($this->hasCompleteExistingInfo($existingInfo)) {
                 $io->section('发现现有扩展配置');
@@ -35,7 +36,7 @@ class ComposerGenerator
                         ['扩展ID', $existingInfo['id'] ?? 'N/A']
                     ]
                 );
-                
+
                 if ($io->confirm('确定使用当前配置?', true)) {
                     return [
                         'packageName' => $existingInfo['name'],
@@ -45,38 +46,38 @@ class ComposerGenerator
                     ];
                 }
             }
-            
+
             // 获取包名
             $defaultPackageName = $existingInfo['name'] ?? null;
             $packageName = $io->ask(
                 '请输入Composer包名 (例如: six-shop/hello 或sixdec/abc)',
                 $defaultPackageName
             );
-            
+
             if (empty($packageName)) {
                 throw new \InvalidArgumentException('包名不能为空!');
             }
-            
+
             // 验证包名格式
             if (!preg_match('/^[a-z0-9-_]+\/[a-z0-9-_]+$/', $packageName)) {
                 throw new \InvalidArgumentException('包名格式不正确! 正确格式: six-shop/hello 或sixdec/abc');
             }
-            
+
             // 获取包描述
             $defaultDescription = $existingInfo['description'] ?? 'A SixShop extension package';
             $description = $io->ask('请输入包描述 (可选)', $defaultDescription);
-            
+
             // 获取命名空间
             $defaultNamespace = $existingInfo['namespace'] ?? $this->generateDefaultNamespace($packageName);
             // 用户界面显示单反斜杠格式,用户输入也是单反斜杠
             $userNamespace = $io->ask('请输入命名空间 (例如: SixShop\\Hello\\)', $defaultNamespace);
             // PHP代码内部使用单反斜杠存储
             $namespace = rtrim($userNamespace, '\\') . '\\';
-            
+
             // 获取扩展ID
             $defaultId = $existingInfo['id'] ?? str_replace('/', '-', $packageName);
             $id = $io->ask('请输入扩展ID (例如: six-shop-hello)', $defaultId);
-            
+
             return [
                 'packageName' => $packageName,
                 'description' => $description,
@@ -88,7 +89,7 @@ class ComposerGenerator
             return null;
         }
     }
-    
+
     /**
      * 生成默认命名空间
      */
@@ -99,7 +100,7 @@ class ComposerGenerator
         $package = $this->convertToNamespace($parts[1]);
         return $vendor . '\\' . $package . '\\';
     }
-    
+
     /**
      * 将字符串转换为命名空间格式
      */
@@ -109,18 +110,18 @@ class ComposerGenerator
         $parts = array_map('ucfirst', $parts);
         return implode('', $parts);
     }
-    
+
     /**
      * 读取现有的composer.json文件以获取默认值
      */
     private function readExistingComposerInfo(?string $targetPath, SymfonyStyle $io): array
     {
         $existingInfo = [];
-        
+
         if ($targetPath === null) {
             return $existingInfo;
         }
-        
+
         if (is_dir($targetPath)) {
             // 首先检查目标目录下是否直接有composer.json文件
             $directComposerPath = $targetPath . '/composer.json';
@@ -130,10 +131,10 @@ class ComposerGenerator
                     return $existingInfo;
                 }
             }
-            
+
             // 然后扫描目标目录下的所有子目录,查找composer.json文件
             $directories = array_filter(glob($targetPath . '/*'), 'is_dir');
-            
+
             foreach ($directories as $dir) {
                 $composerPath = $dir . '/composer.json';
                 if (file_exists($composerPath)) {
@@ -145,27 +146,27 @@ class ComposerGenerator
                 }
             }
         }
-        
+
         return $existingInfo;
     }
-    
+
     /**
      * 解析composer.json文件并提取相关信息
      */
     private function parseComposerFile(string $composerPath, SymfonyStyle $io): array
     {
         $existingInfo = [];
-        
+
         $composerContent = file_get_contents($composerPath);
         if ($composerContent !== false) {
             $composerData = json_decode($composerContent, true);
             if (json_last_error() === JSON_ERROR_NONE && is_array($composerData)) {
                 $io->note('发现现有的composer.json文件: ' . $composerPath);
-                
+
                 // 提取相关信息
                 $existingInfo['name'] = $composerData['name'] ?? null;
                 $existingInfo['description'] = $composerData['description'] ?? null;
-                
+
                 // 从 autoload PSR-4 中提取命名空间
                 if (isset($composerData['autoload']['psr-4'])) {
                     $psr4 = $composerData['autoload']['psr-4'];
@@ -175,25 +176,25 @@ class ComposerGenerator
                         $existingInfo['namespace'] = rtrim($namespaces[0], '\\') . '\\';
                     }
                 }
-                
+
                 // 从 extra.sixshop 中提取ID
                 if (isset($composerData['extra']['sixshop']['id'])) {
                     $existingInfo['id'] = $composerData['extra']['sixshop']['id'];
                 }
             }
         }
-        
+
         return $existingInfo;
     }
-    
+
     /**
      * 检查是否有完整的现有信息
      */
     private function hasCompleteExistingInfo(array $existingInfo): bool
     {
-        return !empty($existingInfo['name']) && 
-               !empty($existingInfo['description']) && 
-               !empty($existingInfo['namespace']) && 
+        return !empty($existingInfo['name']) &&
+               !empty($existingInfo['description']) &&
+               !empty($existingInfo['namespace']) &&
                !empty($existingInfo['id']);
     }
 
@@ -208,7 +209,7 @@ class ComposerGenerator
 
         // 准备模板变量
         $name = $packageName;
-        
+
         // 生成composer.json内容
         ob_start();
         include $this->templatePath;
@@ -264,7 +265,7 @@ class ComposerGenerator
             return false;
         }
     }
-    
+
     /**
      * 完整的从用户输入到生成文件的工作流
      */
@@ -273,13 +274,13 @@ class ComposerGenerator
         try {
             // 获取用户输入
             $packageInfo = $this->gatherPackageInfo($io);
-            
+
             // 如果没有提供目标路径,使用默认路径
             if ($targetPath === null) {
                 $vendorDir = dirname(__DIR__, 5);
                 $targetPath = $vendorDir . '/runtime/extension';
             }
-            
+
             // 生成composer.json
             return $this->createComposerJson(
                 $packageInfo['packageName'],
@@ -294,4 +295,4 @@ class ComposerGenerator
             return false;
         }
     }
-}
+}

+ 48 - 47
src/Generator/ControllerGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -18,24 +19,24 @@ class ControllerGenerator
      * Generate API and Admin controllers
      */
     public function generateControllers(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $success = true;
-            
+
             // Generate API Controller
             $success = $this->generateApiController($tableName, $tableComment, $fields, $extensionInfo, $targetPath, $io) && $success;
-            
+
             // Generate Admin Controller
             $success = $this->generateAdminController($tableName, $tableComment, $fields, $extensionInfo, $targetPath, $io) && $success;
-            
+
             return $success;
-            
+
         } catch (\Exception $e) {
             $io->error('生成控制器时发生错误: ' . $e->getMessage());
             return false;
@@ -46,17 +47,17 @@ class ControllerGenerator
      * Generate API controller
      */
     private function generateApiController(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $controllerName = $this->generateControllerName($tableName);
             $controllerPath = $targetPath . '/src/Controller/Api';
-            
+
             // Ensure controller directory exists
             if (!is_dir($controllerPath)) {
                 if (!mkdir($controllerPath, 0755, true)) {
@@ -64,10 +65,10 @@ class ControllerGenerator
                     return false;
                 }
             }
-            
+
             $fileName = $controllerName . '.php';
             $filePath = $controllerPath . '/' . $fileName;
-            
+
             // Check if file exists
             if (file_exists($filePath)) {
                 if (!$io->confirm("API控制器文件已存在: {$fileName},是否覆盖?", false)) {
@@ -75,16 +76,16 @@ class ControllerGenerator
                     return true;
                 }
             }
-            
+
             // Prepare template variables
             $templateVars = $this->prepareTemplateVariables($tableName, $tableComment, $fields, $extensionInfo, 'Api');
-            
+
             // Generate content using template
             ob_start();
             extract($templateVars);
             include $this->templatesPath . '/src/Controller/Api/Controller.php.tpl.php';
             $content = ob_get_clean();
-            
+
             // Write file
             if (file_put_contents($filePath, $content) !== false) {
                 $io->text("✓ 生成API控制器: src/Controller/Api/{$fileName}");
@@ -93,7 +94,7 @@ class ControllerGenerator
                 $io->error("✗ 无法写入API控制器文件: {$fileName}");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('生成API控制器时发生错误: ' . $e->getMessage());
             return false;
@@ -104,17 +105,17 @@ class ControllerGenerator
      * Generate Admin controller
      */
     private function generateAdminController(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $controllerName = $this->generateControllerName($tableName);
             $controllerPath = $targetPath . '/src/Controller/Admin';
-            
+
             // Ensure controller directory exists
             if (!is_dir($controllerPath)) {
                 if (!mkdir($controllerPath, 0755, true)) {
@@ -122,10 +123,10 @@ class ControllerGenerator
                     return false;
                 }
             }
-            
+
             $fileName = $controllerName . '.php';
             $filePath = $controllerPath . '/' . $fileName;
-            
+
             // Check if file exists
             if (file_exists($filePath)) {
                 if (!$io->confirm("Admin控制器文件已存在: {$fileName},是否覆盖?", false)) {
@@ -133,16 +134,16 @@ class ControllerGenerator
                     return true;
                 }
             }
-            
+
             // Prepare template variables
             $templateVars = $this->prepareTemplateVariables($tableName, $tableComment, $fields, $extensionInfo, 'Admin');
-            
+
             // Generate content using template
             ob_start();
             extract($templateVars);
             include $this->templatesPath . '/src/Controller/Admin/Controller.php.tpl.php';
             $content = ob_get_clean();
-            
+
             // Write file
             if (file_put_contents($filePath, $content) !== false) {
                 $io->text("✓ 生成Admin控制器: src/Controller/Admin/{$fileName}");
@@ -151,7 +152,7 @@ class ControllerGenerator
                 $io->error("✗ 无法写入Admin控制器文件: {$fileName}");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('生成Admin控制器时发生错误: ' . $e->getMessage());
             return false;
@@ -162,20 +163,20 @@ class ControllerGenerator
      * Prepare template variables
      */
     private function prepareTemplateVariables(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
         string $type
     ): array {
         $controllerName = $this->generateControllerName($tableName);
         $modelName = $this->generateModelName($tableName);
         $entityName = $this->generateEntityName($tableName);
         $namespace = rtrim($extensionInfo['namespace'], '\\') . '\\';
-        
+
         // Get searchable fields (string fields that are not too long)
         $searchableFields = $this->getSearchableFields($fields);
-        
+
         return [
             'tableName' => $tableName,
             'tableComment' => $tableComment,
@@ -197,7 +198,7 @@ class ControllerGenerator
         $baseName = $this->generateBaseName($tableName);
         return $baseName . 'Controller';
     }
-    
+
     /**
      * Generate base class name from table name
      */
@@ -205,17 +206,17 @@ class ControllerGenerator
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase and make singular
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
         $name = implode('', $parts);
-        
+
         // Make singular (simple rules)
         if (str_ends_with($name, 's') && !str_ends_with($name, 'ss')) {
             $name = substr($name, 0, -1);
         }
-        
+
         return $name;
     }
 
@@ -244,19 +245,19 @@ class ControllerGenerator
     {
         $searchable = [];
         $excludeFields = ['id', 'create_time', 'update_time', 'delete_time'];
-        
+
         foreach ($fields as $field) {
             if (in_array($field['name'], $excludeFields)) {
                 continue;
             }
-            
+
             // Include string fields with reasonable length and some other types
             if (($field['type'] === 'string' && (!isset($field['length']) || $field['length'] <= 255)) ||
                 in_array($field['type'], ['integer', 'biginteger', 'boolean'])) {
                 $searchable[] = $field;
             }
         }
-        
+
         return $searchable;
     }
-}
+}

+ 17 - 16
src/Generator/DirectoryGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -15,12 +16,12 @@ class DirectoryGenerator
         try {
             // 获取基础路径
             $basePath = $this->getBasePath($packageName, $targetPath);
-            
+
             // 定义需要创建的目录结构
             $directories = $this->getDirectoryStructure();
-            
+
             $io->section('创建目录结构:');
-            
+
             // 创建所有目录
             foreach ($directories as $dir) {
                 $fullPath = $basePath . '/' . $dir;
@@ -28,16 +29,16 @@ class DirectoryGenerator
                     return false;
                 }
             }
-            
+
             $io->success('目录结构创建成功!');
             return true;
-            
+
         } catch (\Exception $e) {
             $io->error('创建目录结构时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-    
+
     /**
      * 获取扩展基础路径
      */
@@ -46,12 +47,12 @@ class DirectoryGenerator
         if ($targetPath !== null) {
             return $targetPath;
         }
-        
+
         // 默认路径
         $vendorDir = dirname(__DIR__, 5); // 从当前文件位置向上5级到vendor上级目录
         return $vendorDir . '/runtime/extension/' . $packageName;
     }
-    
+
     /**
      * 定义扩展的目录结构(参考 six-shop/eav)
      */
@@ -77,7 +78,7 @@ class DirectoryGenerator
             'docs'
         ];
     }
-    
+
     /**
      * 创建单个目录
      */
@@ -87,7 +88,7 @@ class DirectoryGenerator
             $io->text("目录已存在: $path");
             return true;
         }
-        
+
         if (mkdir($path, 0755, true)) {
             $io->text("✓ 创建目录: $path");
             return true;
@@ -96,7 +97,7 @@ class DirectoryGenerator
             return false;
         }
     }
-    
+
     /**
      * 检查目录是否已存在
      */
@@ -105,7 +106,7 @@ class DirectoryGenerator
         $basePath = $this->getBasePath($packageName, $targetPath);
         return is_dir($basePath);
     }
-    
+
     /**
      * 获取目录结构信息
      */
@@ -113,13 +114,13 @@ class DirectoryGenerator
     {
         $basePath = $this->getBasePath($packageName, $targetPath);
         $directories = $this->getDirectoryStructure();
-        
+
         $info = [
             'basePath' => $basePath,
             'directories' => [],
             'exists' => $this->directoryExists($packageName, $targetPath)
         ];
-        
+
         foreach ($directories as $dir) {
             $fullPath = $basePath . '/' . $dir;
             $info['directories'][$dir] = [
@@ -127,7 +128,7 @@ class DirectoryGenerator
                 'exists' => is_dir($fullPath)
             ];
         }
-        
+
         return $info;
     }
-}
+}

+ 24 - 23
src/Generator/EntityGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -18,17 +19,17 @@ class EntityGenerator
      * Generate entity file
      */
     public function generateEntity(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $entityName = $this->generateEntityName($tableName);
             $entityPath = $targetPath . '/src/Entity';
-            
+
             // Ensure entity directory exists
             if (!is_dir($entityPath)) {
                 if (!mkdir($entityPath, 0755, true)) {
@@ -36,10 +37,10 @@ class EntityGenerator
                     return false;
                 }
             }
-            
+
             $fileName = $entityName . '.php';
             $filePath = $entityPath . '/' . $fileName;
-            
+
             // Check if file exists
             if (file_exists($filePath)) {
                 if (!$io->confirm("实体文件已存在: {$fileName},是否覆盖?", false)) {
@@ -47,18 +48,18 @@ class EntityGenerator
                     return true;
                 }
             }
-            
+
             // Prepare template variables
             $entityName = $this->generateEntityName($tableName);
             $modelName = $this->generateModelName($tableName);
             $namespace = rtrim($extensionInfo['namespace'], '\\') . '\\';
             $requiredFields = $this->getRequiredFields($fields);
-            
+
             // Generate content using template
             ob_start();
             include $this->templatesPath . '/src/Entity/Entity.php.tpl.php';
             $content = ob_get_clean();
-            
+
             // Write file
             if (file_put_contents($filePath, $content) !== false) {
                 $io->text("✓ 生成实体文件: src/Entity/{$fileName}");
@@ -67,13 +68,13 @@ class EntityGenerator
                 $io->error("✗ 无法写入实体文件: {$fileName}");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('生成实体文件时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-    
+
     /**
      * Generate entity class name from table name
      */
@@ -81,14 +82,14 @@ class EntityGenerator
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
-        
+
         return implode('', $parts) . 'Entity';
     }
-    
+
     /**
      * Generate model class name from table name
      */
@@ -96,14 +97,14 @@ class EntityGenerator
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
-        
+
         return implode('', $parts) . 'Model';
     }
-    
+
     /**
      * Get required fields for validation
      */
@@ -111,14 +112,14 @@ class EntityGenerator
     {
         $required = [];
         $excludeFields = ['id', 'create_time', 'update_time', 'delete_time'];
-        
+
         foreach ($fields as $field) {
-            if (!in_array($field['name'], $excludeFields) && 
+            if (!in_array($field['name'], $excludeFields) &&
                 (!isset($field['null']) || !$field['null'])) {
                 $required[] = $field;
             }
         }
-        
+
         return $required;
     }
-}
+}

+ 2 - 1
src/Generator/GitignoreGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -78,4 +79,4 @@ class GitignoreGenerator
             return false;
         }
     }
-}
+}

+ 36 - 27
src/Generator/MigrationGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -39,7 +40,9 @@ class MigrationGenerator
             // Ensure migrations directory exists
             if (!is_dir($this->migrationsPath)) {
                 if (!mkdir($this->migrationsPath, 0755, true)) {
-                    if ($io) $io->error("无法创建迁移目录: {$this->migrationsPath}");
+                    if ($io) {
+                        $io->error("无法创建迁移目录: {$this->migrationsPath}");
+                    }
                     return false;
                 }
             }
@@ -47,7 +50,7 @@ class MigrationGenerator
             // Generate migration name and class name
             $migrationName = $this->generateMigrationName($tableName, $action);
             $className = $this->generateClassName($migrationName);
-            
+
             // Generate timestamp
             $timestamp = date('YmdHis');
             $filename = $timestamp . '_' . $migrationName . '.php';
@@ -58,15 +61,21 @@ class MigrationGenerator
 
             // Write migration file
             if (file_put_contents($filePath, $content) !== false) {
-                if ($io) $io->success("Migration created: {$filename}");
+                if ($io) {
+                    $io->success("Migration created: {$filename}");
+                }
                 return true;
             } else {
-                if ($io) $io->error("无法写入迁移文件: {$filename}");
+                if ($io) {
+                    $io->error("无法写入迁移文件: {$filename}");
+                }
                 return false;
             }
 
         } catch (\Exception $e) {
-            if ($io) $io->error('生成迁移文件时发生错误: ' . $e->getMessage());
+            if ($io) {
+                $io->error('生成迁移文件时发生错误: ' . $e->getMessage());
+            }
             return false;
         }
     }
@@ -108,7 +117,7 @@ class MigrationGenerator
         $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";
@@ -123,7 +132,7 @@ class MigrationGenerator
         $content .= "     */\n";
         $content .= "    public function change(): void\n";
         $content .= "    {\n";
-        
+
         switch ($action) {
             case 'create':
                 $content .= $this->generateCreateTableContent($tableName, $fields);
@@ -138,7 +147,7 @@ class MigrationGenerator
                 $content .= $this->generateModifyTableContent($tableName, $fields);
                 break;
         }
-        
+
         $content .= "    }\n";
         $content .= "}\n";
 
@@ -151,13 +160,13 @@ class MigrationGenerator
     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);
@@ -177,7 +186,7 @@ class MigrationGenerator
         }
 
         $content .= "        \$table->create();\n";
-        
+
         return $content;
     }
 
@@ -187,21 +196,21 @@ class MigrationGenerator
     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;
     }
 
@@ -211,14 +220,14 @@ class MigrationGenerator
     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;
     }
 
@@ -230,7 +239,7 @@ class MigrationGenerator
         $content = "        \$table = \$this->table('{$tableName}');\n";
         $content .= "        // Add your table modifications here\n";
         $content .= "        \$table->update();\n";
-        
+
         return $content;
     }
 
@@ -240,29 +249,29 @@ class MigrationGenerator
     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'])) {
@@ -272,7 +281,7 @@ class MigrationGenerator
                 $options['scale'] = (int)$field['scale'];
             }
         }
-        
+
         return $options;
     }
 
@@ -319,4 +328,4 @@ class MigrationGenerator
     {
         return $this->migrationsPath;
     }
-}
+}

+ 31 - 30
src/Generator/ModelGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -18,17 +19,17 @@ class ModelGenerator
      * Generate model file
      */
     public function generateModel(
-        string $tableName, 
-        string $tableComment, 
-        array $fields, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $fields,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $modelName = $this->generateModelName($tableName);
             $modelPath = $targetPath . '/src/Model';
-            
+
             // Ensure model directory exists
             if (!is_dir($modelPath)) {
                 if (!mkdir($modelPath, 0755, true)) {
@@ -36,10 +37,10 @@ class ModelGenerator
                     return false;
                 }
             }
-            
+
             $fileName = $modelName . '.php';
             $filePath = $modelPath . '/' . $fileName;
-            
+
             // Check if file exists
             if (file_exists($filePath)) {
                 if (!$io->confirm("模型文件已存在: {$fileName},是否覆盖?", false)) {
@@ -47,18 +48,18 @@ class ModelGenerator
                     return true;
                 }
             }
-            
+
             // Prepare template variables
             $namespace = rtrim($extensionInfo['namespace'], '\\') . '\\';
             $fillableFields = $this->getFillableFields($fields);
             $requiredFields = $this->getRequiredFields($fields);
             $relationshipFields = $this->getRelationshipFields($fields);
-            
+
             // Generate content using template
             ob_start();
             include $this->templatesPath . '/src/Model/Model.php.tpl.php';
             $content = ob_get_clean();
-            
+
             // Write file
             if (file_put_contents($filePath, $content) !== false) {
                 $io->text("✓ 生成模型文件: src/Model/{$fileName}");
@@ -67,13 +68,13 @@ class ModelGenerator
                 $io->error("✗ 无法写入模型文件: {$fileName}");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('生成模型文件时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-    
+
     /**
      * Generate model class name from table name
      */
@@ -81,14 +82,14 @@ class ModelGenerator
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
-        
+
         return implode('', $parts) . 'Model';
     }
-    
+
     /**
      * Generate model class name for relationships (without Model suffix)
      */
@@ -96,14 +97,14 @@ class ModelGenerator
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
-        
+
         return implode('', $parts) . 'Model';
     }
-    
+
     /**
      * Get fillable fields (excluding auto-generated fields)
      */
@@ -111,16 +112,16 @@ class ModelGenerator
     {
         $fillable = [];
         $excludeFields = ['id', 'create_time', 'update_time', 'delete_time'];
-        
+
         foreach ($fields as $field) {
             if (!in_array($field['name'], $excludeFields)) {
                 $fillable[] = $field['name'];
             }
         }
-        
+
         return $fillable;
     }
-    
+
     /**
      * Get required fields for validation
      */
@@ -128,29 +129,29 @@ class ModelGenerator
     {
         $required = [];
         $excludeFields = ['id', 'create_time', 'update_time', 'delete_time'];
-        
+
         foreach ($fields as $field) {
-            if (!in_array($field['name'], $excludeFields) && 
+            if (!in_array($field['name'], $excludeFields) &&
                 (!isset($field['null']) || !$field['null'])) {
                 $required[] = $field;
             }
         }
-        
+
         return $required;
     }
-    
+
     /**
      * Get relationship fields (foreign keys)
      */
     private function getRelationshipFields(array $fields): array
     {
         $relationships = [];
-        
+
         foreach ($fields as $field) {
             if (str_ends_with($field['name'], '_id') && $field['name'] !== 'id') {
                 $relationName = str_replace('_id', '', $field['name']);
                 $modelName = $this->generateModelNameForRelation($relationName);
-                
+
                 $relationships[] = [
                     'method' => $relationName,
                     'model' => $modelName,
@@ -160,7 +161,7 @@ class ModelGenerator
                 ];
             }
         }
-        
+
         return $relationships;
     }
-}
+}

+ 19 - 18
src/Generator/PhpCodeGenerator.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -8,7 +9,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
 class PhpCodeGenerator
 {
     private string $templatesPath;
-    
+
     /**
      * 排除的模板文件,这些模板由专门的生成器处理(一对多关系)
      */
@@ -31,7 +32,7 @@ class PhpCodeGenerator
     {
         try {
             $basePath = $this->getBasePath($packageName, $targetPath);
-            
+
             // 准备模板变量
             $variables = [
                 'namespace' => $namespace,
@@ -42,18 +43,18 @@ class PhpCodeGenerator
 
             // 获取所有需要生成的PHP文件模板
             $templateFiles = $this->getTemplateFiles();
-            
+
             $io->section('生成PHP代码文件:');
-            
+
             foreach ($templateFiles as $templateFile) {
                 if (!$this->generateFileFromTemplate($templateFile, $basePath, $variables, $io)) {
                     return false;
                 }
             }
-            
+
             $io->success('PHP代码文件生成完成!');
             return true;
-            
+
         } catch (\Exception $e) {
             $io->error('生成PHP代码文件时发生错误: ' . $e->getMessage());
             return false;
@@ -68,7 +69,7 @@ class PhpCodeGenerator
         if ($targetPath !== null) {
             return $targetPath;
         }
-        
+
         // 默认路径
         $vendorDir = dirname(__DIR__, 5);
         return $vendorDir . '/runtime/extension/' . $packageName;
@@ -107,7 +108,7 @@ class PhpCodeGenerator
                 if ($this->isExcludedTemplate($currentRelativePath)) {
                     continue;
                 }
-                
+
                 // 递归扫描子目录
                 $this->scanTemplateDirectory($fullPath, $currentRelativePath, $templates);
             } elseif (is_file($fullPath) && str_ends_with($item, '.php.tpl.php')) {
@@ -115,7 +116,7 @@ class PhpCodeGenerator
                 if ($this->isExcludedTemplate($currentRelativePath)) {
                     continue;
                 }
-                
+
                 // 添加模板文件
                 $templates[] = [
                     'template' => $currentRelativePath,
@@ -133,7 +134,7 @@ class PhpCodeGenerator
     {
         $templatePath = $templateInfo['fullPath'];
         $targetPath = $basePath . '/' . $templateInfo['target'];
-        
+
         // 确保目标目录存在
         $targetDir = dirname($targetPath);
         if (!is_dir($targetDir)) {
@@ -180,7 +181,7 @@ class PhpCodeGenerator
     {
         return $this->getTemplateFiles();
     }
-    
+
     /**
      * 检查模板是否在排除列表中
      */
@@ -194,7 +195,7 @@ class PhpCodeGenerator
         }
         return false;
     }
-    
+
     /**
      * 设置排除的模板列表
      */
@@ -203,7 +204,7 @@ class PhpCodeGenerator
         $this->excludedTemplates = $excludedTemplates;
         return $this;
     }
-    
+
     /**
      * 添加排除的模板
      */
@@ -214,19 +215,19 @@ class PhpCodeGenerator
         }
         return $this;
     }
-    
+
     /**
      * 移除排除的模板
      */
     public function removeExcludedTemplate(string $templatePath): self
     {
         $this->excludedTemplates = array_filter(
-            $this->excludedTemplates, 
-            fn($excluded) => $excluded !== $templatePath
+            $this->excludedTemplates,
+            fn ($excluded) => $excluded !== $templatePath
         );
         return $this;
     }
-    
+
     /**
      * 获取当前排除的模板列表
      */
@@ -234,4 +235,4 @@ class PhpCodeGenerator
     {
         return $this->excludedTemplates;
     }
-}
+}

+ 54 - 53
src/Generator/RouteUpdater.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Generator;
@@ -11,23 +12,23 @@ class RouteUpdater
      * Update route files with new resource routes
      */
     public function updateRoutes(
-        string $tableName, 
-        string $tableComment, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $success = true;
-            
+
             // Update API routes
             $success = $this->updateApiRoutes($tableName, $tableComment, $extensionInfo, $targetPath, $io) && $success;
-            
+
             // Update Admin routes
             $success = $this->updateAdminRoutes($tableName, $tableComment, $extensionInfo, $targetPath, $io) && $success;
-            
+
             return $success;
-            
+
         } catch (\Exception $e) {
             $io->error('更新路由文件时发生错误: ' . $e->getMessage());
             return false;
@@ -38,44 +39,44 @@ class RouteUpdater
      * Update API routes file
      */
     private function updateApiRoutes(
-        string $tableName, 
-        string $tableComment, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $routeFile = $targetPath . '/route/api.php';
-            
+
             if (!file_exists($routeFile)) {
                 $io->warning("API路由文件不存在: {$routeFile}");
                 return true; // Not an error, just skip
             }
-            
+
             $controllerName = $this->generateControllerName($tableName);
             $resourceName = $this->generateResourceName($tableName);
             $namespace = rtrim($extensionInfo['namespace'], '\\');
-            
+
             // Generate route code
             $routeCode = $this->generateApiRouteCode(
-                $controllerName, 
-                $resourceName, 
-                $namespace, 
+                $controllerName,
+                $resourceName,
+                $namespace,
                 $tableComment
             );
-            
+
             // Read current file content
             $currentContent = file_get_contents($routeFile);
-            
+
             // Check if routes already exist
             if (strpos($currentContent, "'{$resourceName}'") !== false) {
                 $io->text("API路由已存在,跳过: {$resourceName}");
                 return true;
             }
-            
+
             // Append new routes
             $newContent = rtrim($currentContent) . "\n\n" . $routeCode;
-            
+
             // Write updated content
             if (file_put_contents($routeFile, $newContent) !== false) {
                 $io->text("✓ 更新API路由文件: route/api.php");
@@ -84,7 +85,7 @@ class RouteUpdater
                 $io->error("✗ 无法写入API路由文件");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('更新API路由时发生错误: ' . $e->getMessage());
             return false;
@@ -95,44 +96,44 @@ class RouteUpdater
      * Update Admin routes file
      */
     private function updateAdminRoutes(
-        string $tableName, 
-        string $tableComment, 
-        array $extensionInfo, 
-        string $targetPath, 
+        string $tableName,
+        string $tableComment,
+        array $extensionInfo,
+        string $targetPath,
         SymfonyStyle $io
     ): bool {
         try {
             $routeFile = $targetPath . '/route/admin.php';
-            
+
             if (!file_exists($routeFile)) {
                 $io->warning("Admin路由文件不存在: {$routeFile}");
                 return true; // Not an error, just skip
             }
-            
+
             $controllerName = $this->generateControllerName($tableName);
             $resourceName = $this->generateResourceName($tableName);
             $namespace = rtrim($extensionInfo['namespace'], '\\');
-            
+
             // Generate route code
             $routeCode = $this->generateAdminRouteCode(
-                $controllerName, 
-                $resourceName, 
-                $namespace, 
+                $controllerName,
+                $resourceName,
+                $namespace,
                 $tableComment
             );
-            
+
             // Read current file content
             $currentContent = file_get_contents($routeFile);
-            
+
             // Check if routes already exist
             if (strpos($currentContent, "'{$resourceName}'") !== false) {
                 $io->text("Admin路由已存在,跳过: {$resourceName}");
                 return true;
             }
-            
+
             // Append new routes
             $newContent = rtrim($currentContent) . "\n\n" . $routeCode;
-            
+
             // Write updated content
             if (file_put_contents($routeFile, $newContent) !== false) {
                 $io->text("✓ 更新Admin路由文件: route/admin.php");
@@ -141,7 +142,7 @@ class RouteUpdater
                 $io->error("✗ 无法写入Admin路由文件");
                 return false;
             }
-            
+
         } catch (\Exception $e) {
             $io->error('更新Admin路由时发生错误: ' . $e->getMessage());
             return false;
@@ -152,16 +153,16 @@ class RouteUpdater
      * Generate API route code
      */
     private function generateApiRouteCode(
-        string $controllerName, 
-        string $resourceName, 
-        string $namespace, 
+        string $controllerName,
+        string $resourceName,
+        string $namespace,
         string $tableComment
     ): string {
         $controllerClass = "{$namespace}\\Controller\\Api\\{$controllerName}";
-        
+
         $code = "// {$tableComment}相关路由\n";
         $code .= "Route::resource('{$resourceName}', {$controllerClass}::class);";
-        
+
         return $code;
     }
 
@@ -169,16 +170,16 @@ class RouteUpdater
      * Generate Admin route code
      */
     private function generateAdminRouteCode(
-        string $controllerName, 
-        string $resourceName, 
-        string $namespace, 
+        string $controllerName,
+        string $resourceName,
+        string $namespace,
         string $tableComment
     ): string {
         $controllerClass = "{$namespace}\\Controller\\Admin\\{$controllerName}";
-        
+
         $code = "// {$tableComment}管理路由\n";
         $code .= "Route::resource('{$resourceName}', {$controllerClass}::class);";
-        
+
         return $code;
     }
 
@@ -189,17 +190,17 @@ class RouteUpdater
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Convert to PascalCase and make singular
         $parts = explode('_', $tableName);
         $parts = array_map('ucfirst', $parts);
         $name = implode('', $parts);
-        
+
         // Make singular (simple rules)
         if (str_ends_with($name, 's') && !str_ends_with($name, 'ss')) {
             $name = substr($name, 0, -1);
         }
-        
+
         return $name . 'Controller';
     }
 
@@ -210,8 +211,8 @@ class RouteUpdater
     {
         // Remove common prefixes
         $tableName = preg_replace('/^(tbl_|tb_|t_)/', '', $tableName);
-        
+
         // Keep underscore format for URLs
         return $tableName;
     }
-}
+}

+ 20 - 20
src/Maker.php

@@ -1,12 +1,12 @@
 <?php
+
 declare(strict_types=1);
+
 namespace SixShop\MakerBundle;
 
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Style\SymfonyStyle;
 use SixShop\MakerBundle\Generator\ComposerGenerator;
 use SixShop\MakerBundle\Generator\DirectoryGenerator;
@@ -22,7 +22,7 @@ class Maker extends Command
     private DirectoryGenerator $directoryGenerator;
     private GitignoreGenerator $gitignoreGenerator;
     private PhpCodeGenerator $phpCodeGenerator;
-    
+
     public function __construct()
     {
         parent::__construct();
@@ -41,19 +41,19 @@ class Maker extends Command
     protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $io = new SymfonyStyle($input, $output);
-        
+
         // 获取用户输入的目标路径
         $targetPath = $this->getTargetPath($io);
         if (!$targetPath) {
             return Command::FAILURE;
         }
-        
+
         // 获取用户输入的包信息(现在会检测目标路径下的composer.json)
         $packageInfo = $this->composerGenerator->gatherPackageInfo($io, $targetPath);
         if (!$packageInfo) {
             return Command::FAILURE;
         }
-        
+
         // 生成 composer.json 文件
         $io->section('生成 composer.json 文件');
         if (!$this->composerGenerator->createComposerJson(
@@ -66,19 +66,19 @@ class Maker extends Command
         )) {
             return Command::FAILURE;
         }
-        
+
         // 创建目录结构
         $io->section('创建目录结构');
         if (!$this->directoryGenerator->createDirectoryStructure($packageInfo['packageName'], $targetPath, $io)) {
             return Command::FAILURE;
         }
-        
+
         // 生成 .gitignore 文件
         $io->section('生成 .gitignore 文件');
         if (!$this->gitignoreGenerator->createGitignore($packageInfo['packageName'], $targetPath, $io)) {
             return Command::FAILURE;
         }
-        
+
         // 生成 PHP 代码文件
         $io->section('生成 PHP 代码文件');
         if (!$this->phpCodeGenerator->generatePhpFiles(
@@ -91,11 +91,11 @@ class Maker extends Command
         )) {
             return Command::FAILURE;
         }
-        
+
         $io->success('扩展创建完成!');
         return Command::SUCCESS;
     }
-    
+
     /**
      * 获取用户输入的目标路径
      */
@@ -104,16 +104,16 @@ class Maker extends Command
         try {
             // 获取当前工作目录作为默认值
             $defaultPath = getcwd();
-            
+
             $targetPath = $io->ask(
                 '请输入扩展生成的目标路径 (绝对路径或相对路径)',
                 $defaultPath
             );
-            
+
             if (empty($targetPath)) {
                 throw new \InvalidArgumentException('目标路径不能为空!');
             }
-            
+
             // 转换为绝对路径
             $absolutePath = realpath($targetPath);
             if ($absolutePath === false) {
@@ -126,24 +126,24 @@ class Maker extends Command
                 }
                 $absolutePath = realpath($targetPath);
             }
-            
+
             // 检查路径是否可写
             if (!is_writable($absolutePath)) {
                 throw new \InvalidArgumentException('目标路径不可写: ' . $absolutePath);
             }
-            
+
             $io->note('将在以下路径生成扩展: ' . $absolutePath);
-            
+
             // 确认路径
             if (!$io->confirm('确认使用此路径?', true)) {
                 return $this->getTargetPath($io); // 递归重新获取路径
             }
-            
+
             return $absolutePath;
-            
+
         } catch (\InvalidArgumentException $e) {
             $io->error($e->getMessage());
             return null;
         }
     }
-}
+}

+ 92 - 92
src/MigrationMaker.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle;
@@ -6,7 +7,6 @@ namespace SixShop\MakerBundle;
 use Symfony\Component\Console\Command\Command;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Input\InputArgument;
 use Symfony\Component\Console\Input\InputOption;
 use Symfony\Component\Console\Style\SymfonyStyle;
 use SixShop\MakerBundle\Generator\MigrationGenerator;
@@ -20,14 +20,14 @@ class MigrationMaker extends Command
 {
     protected static $defaultName = 'create_migration';
     protected static $defaultDescription = '生成数据库迁移文件、模型、实体、控制器和路由';
-    
+
     private MigrationGenerator $migrationGenerator;
     private ComposerGenerator $composerGenerator;
     private ModelGenerator $modelGenerator;
     private EntityGenerator $entityGenerator;
     private ControllerGenerator $controllerGenerator;
     private RouteUpdater $routeUpdater;
-    
+
     public function __construct()
     {
         parent::__construct();
@@ -50,19 +50,19 @@ class MigrationMaker extends Command
     protected function execute(InputInterface $input, OutputInterface $output): int
     {
         $io = new SymfonyStyle($input, $output);
-        
+
         // Get target path for the SixShop extension
         $targetPath = $this->getTargetPath($io);
         if (!$targetPath) {
             return Command::FAILURE;
         }
-        
+
         // Get extension information from composer.json
         $extensionInfo = $this->composerGenerator->gatherPackageInfo($io, $targetPath);
         if (!$extensionInfo) {
             return Command::FAILURE;
         }
-        
+
         $io->title("生成数据库迁移文件、模型、实体、控制器和路由");
         $io->section("扩展信息");
         $io->table(
@@ -73,29 +73,29 @@ class MigrationMaker extends Command
                 ['扩展ID', $extensionInfo['id']]
             ]
         );
-        
+
         // Get table information
         $tableName = $io->ask('请输入表名');
         if (empty($tableName)) {
             $io->error('表名不能为空!');
             return Command::FAILURE;
         }
-        
+
         $tableComment = $io->ask('请输入表注释 (可选)', '');
-        
+
         $action = $input->getOption('action');
-        
+
         // Set migration path in the migrations directory of the target path
         $migrationPath = $targetPath . '/database/migrations';
         $this->migrationGenerator->setMigrationsPath($migrationPath);
-        
+
         $io->section("表名: {$tableName}");
         $io->section("操作类型: {$action}");
         if (!empty($tableComment)) {
             $io->section("表注释: {$tableComment}");
         }
         $io->note("迁移文件将保存在: {$migrationPath}");
-        
+
         // Get field definitions based on action
         $fields = [];
         switch ($action) {
@@ -115,42 +115,42 @@ class MigrationMaker extends Command
                 $io->error("不支持的操作类型: {$action}");
                 return Command::FAILURE;
         }
-        
+
         // Generate migration
         if (!$this->migrationGenerator->generateMigration($tableName, $fields, $action, $io)) {
             return Command::FAILURE;
         }
-        
+
         // Generate model and entity if creating a new table
         if ($action === 'create') {
             $io->section('生成相关模型和实体文件');
-            
+
             if (!$this->generateModelAndEntity($tableName, $tableComment, $fields, $extensionInfo, $targetPath, $io)) {
                 $io->warning('迁移文件生成成功,但模型/实体生成失败');
                 return Command::FAILURE;
             }
-            
+
             // Generate controllers after entity generation
             $io->section('生成控制器文件');
-            
+
             if (!$this->generateControllers($tableName, $tableComment, $fields, $extensionInfo, $targetPath, $io)) {
                 $io->warning('模型/实体生成成功,但控制器生成失败');
                 return Command::FAILURE;
             }
-            
+
             // Update route files after controller generation
             $io->section('更新路由文件');
-            
+
             if (!$this->updateRoutes($tableName, $tableComment, $extensionInfo, $targetPath, $io)) {
                 $io->warning('控制器生成成功,但路由更新失败');
                 return Command::FAILURE;
             }
         }
-        
+
         $io->success('迁移文件和相关文件生成完成!');
         return Command::SUCCESS;
     }
-    
+
     /**
      * Get fields for create table action
      */
@@ -158,34 +158,34 @@ class MigrationMaker extends Command
     {
         $fields = [];
         $supportedTypes = $this->migrationGenerator->getSupportedFieldTypes();
-        
+
         $io->section('定义表字段');
         $io->note('支持的字段类型: ' . implode(', ', $supportedTypes));
-        
+
         while (true) {
             $fieldName = $io->ask('字段名 (回车结束字段定义)');
             if (empty($fieldName)) {
                 break;
             }
-            
+
             $fieldType = $io->choice('字段类型', $supportedTypes, 'string');
-            
+
             $field = [
                 'name' => $fieldName,
                 'type' => $fieldType
             ];
-            
+
             // Get field options
             $field = array_merge($field, $this->getFieldOptions($io, $fieldType));
-            
+
             $fields[] = $field;
-            
+
             $io->text("已添加字段: {$fieldName} ({$fieldType})");
         }
-        
+
         return $fields;
     }
-    
+
     /**
      * Get fields for add column action
      */
@@ -193,29 +193,29 @@ class MigrationMaker extends Command
     {
         return $this->getCreateTableFields($io); // Same logic for adding columns
     }
-    
+
     /**
      * Get fields for drop column action
      */
     private function getDropColumnFields(SymfonyStyle $io): array
     {
         $fields = [];
-        
+
         $io->section('选择要删除的字段');
-        
+
         while (true) {
             $fieldName = $io->ask('要删除的字段名 (回车结束)');
             if (empty($fieldName)) {
                 break;
             }
-            
+
             $fields[] = ['name' => $fieldName];
             $io->text("已添加删除字段: {$fieldName}");
         }
-        
+
         return $fields;
     }
-    
+
     /**
      * Get fields for modify table action
      */
@@ -225,14 +225,14 @@ class MigrationMaker extends Command
         $io->note('此操作将生成一个空的迁移模板,您需要手动添加修改逻辑');
         return [];
     }
-    
+
     /**
      * Get field options based on field type
      */
     private function getFieldOptions(SymfonyStyle $io, string $fieldType): array
     {
         $options = [];
-        
+
         // Length/limit
         if (in_array($fieldType, ['string', 'binary'])) {
             $length = $io->ask('字段长度 (可选)', null);
@@ -240,7 +240,7 @@ class MigrationMaker extends Command
                 $options['length'] = (int)$length;
             }
         }
-        
+
         // Decimal precision and scale
         if ($fieldType === 'decimal') {
             $precision = $io->ask('精度 (总位数)', '10');
@@ -248,10 +248,10 @@ class MigrationMaker extends Command
             $options['precision'] = (int)$precision;
             $options['scale'] = (int)$scale;
         }
-        
+
         // Nullable
         $options['null'] = $io->confirm('允许为空?', true);
-        
+
         // Default value
         $defaultValue = $io->ask('默认值 (可选)', null);
         if ($defaultValue !== null) {
@@ -266,22 +266,22 @@ class MigrationMaker extends Command
                 $options['default'] = $defaultValue;
             }
         }
-        
+
         // Comment
         $comment = $io->ask('字段备注 (可选)', null);
         if ($comment !== null) {
             $options['comment'] = $comment;
         }
-        
+
         // Index options
         $options['index'] = $io->confirm('创建索引?', false);
         if (!$options['index']) {
             $options['unique'] = $io->confirm('创建唯一索引?', false);
         }
-        
+
         return $options;
     }
-    
+
     /**
      * Interactive field builder
      */
@@ -289,29 +289,29 @@ class MigrationMaker extends Command
     {
         $fields = [];
         $supportedTypes = $this->migrationGenerator->getSupportedFieldTypes();
-        
+
         $io->section('交互式字段构建器');
         $io->note('您可以逐个定义表字段,支持的类型: ' . implode(', ', $supportedTypes));
-        
+
         $continueAdding = true;
         while ($continueAdding) {
             $fieldName = $io->ask('字段名');
             if (empty($fieldName)) {
                 break;
             }
-            
+
             $fieldType = $io->choice('字段类型', $supportedTypes, 'string');
-            
+
             $field = [
                 'name' => $fieldName,
                 'type' => $fieldType
             ];
-            
+
             // Get additional options
             $field = array_merge($field, $this->getFieldOptions($io, $fieldType));
-            
+
             $fields[] = $field;
-            
+
             // Show field summary
             $summary = "字段: {$field['name']} ({$field['type']})";
             if (isset($field['length'])) {
@@ -323,15 +323,15 @@ class MigrationMaker extends Command
             if (isset($field['default'])) {
                 $summary .= " 默认:{$field['default']}";
             }
-            
+
             $io->text("✓ 已添加 - {$summary}");
-            
+
             $continueAdding = $io->confirm('继续添加字段?', true);
         }
-        
+
         return $fields;
     }
-    
+
     /**
      * 获取用户输入的目标路径
      */
@@ -340,16 +340,16 @@ class MigrationMaker extends Command
         try {
             // 获取当前工作目录作为默认值
             $defaultPath = getcwd();
-            
+
             $targetPath = $io->ask(
                 '请输入SixShop扩展目标路径 (绝对路径或相对路径)',
                 $defaultPath
             );
-            
+
             if (empty($targetPath)) {
                 throw new \InvalidArgumentException('目标路径不能为空!');
             }
-            
+
             // 转换为绝对路径
             $absolutePath = realpath($targetPath);
             if ($absolutePath === false) {
@@ -362,27 +362,27 @@ class MigrationMaker extends Command
                 }
                 $absolutePath = realpath($targetPath);
             }
-            
+
             // 检查路径是否可写
             if (!is_writable($absolutePath)) {
                 throw new \InvalidArgumentException('目标路径不可写: ' . $absolutePath);
             }
-            
+
             $io->note('将在以下扩展路径生成迁移文件: ' . $absolutePath . '/database/migrations');
-            
+
             // 确认路径
             if (!$io->confirm('确认使用此路径?', true)) {
                 return $this->getTargetPath($io); // 递归重新获取路径
             }
-            
+
             return $absolutePath;
-            
+
         } catch (\InvalidArgumentException $e) {
             $io->error($e->getMessage());
             return null;
         }
     }
-    
+
     /**
      * 生成模型和实体文件
      */
@@ -391,32 +391,32 @@ class MigrationMaker extends Command
         try {
             // Generate model file
             $modelResult = $this->modelGenerator->generateModel(
-                $tableName, 
-                $tableComment, 
-                $fields, 
-                $extensionInfo, 
-                $targetPath, 
+                $tableName,
+                $tableComment,
+                $fields,
+                $extensionInfo,
+                $targetPath,
                 $io
             );
-            
+
             // Generate entity file
             $entityResult = $this->entityGenerator->generateEntity(
-                $tableName, 
-                $tableComment, 
-                $fields, 
-                $extensionInfo, 
-                $targetPath, 
+                $tableName,
+                $tableComment,
+                $fields,
+                $extensionInfo,
+                $targetPath,
                 $io
             );
-            
+
             return $modelResult && $entityResult;
-            
+
         } catch (\Exception $e) {
             $io->error('生成模型和实体文件时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-    
+
     /**
      * 生成控制器文件
      */
@@ -424,20 +424,20 @@ class MigrationMaker extends Command
     {
         try {
             return $this->controllerGenerator->generateControllers(
-                $tableName, 
-                $tableComment, 
-                $fields, 
-                $extensionInfo, 
-                $targetPath, 
+                $tableName,
+                $tableComment,
+                $fields,
+                $extensionInfo,
+                $targetPath,
                 $io
             );
-            
+
         } catch (\Exception $e) {
             $io->error('生成控制器文件时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-    
+
     /**
      * 更新路由文件
      */
@@ -445,16 +445,16 @@ class MigrationMaker extends Command
     {
         try {
             return $this->routeUpdater->updateRoutes(
-                $tableName, 
-                $tableComment, 
-                $extensionInfo, 
-                $targetPath, 
+                $tableName,
+                $tableComment,
+                $extensionInfo,
+                $targetPath,
                 $io
             );
-            
+
         } catch (\Exception $e) {
             $io->error('更新路由文件时发生错误: ' . $e->getMessage());
             return false;
         }
     }
-}
+}

+ 1 - 1
templates/src/Entity/Entity.php.tpl.php

@@ -20,7 +20,7 @@ use function SixShop\Core\throw_logic_exception;
  */
 class <?= $entityName ?> extends BaseEntity
 {
-<?php 
+<?php
 $methodName = ucfirst(str_replace('_', '', $tableName));
 // Generate singular method name based on table name
 $singularMethodName = $methodName;

+ 33 - 25
tests/Unit/Generator/ComposerGeneratorTest.php

@@ -5,8 +5,6 @@ namespace SixShop\MakerBundle\Tests\Unit\Generator;
 use PHPUnit\Framework\TestCase;
 use SixShop\MakerBundle\Generator\ComposerGenerator;
 use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
 
 class ComposerGeneratorTest extends TestCase
 {
@@ -18,10 +16,10 @@ class ComposerGeneratorTest extends TestCase
     {
         $this->testDir = sys_get_temp_dir() . '/composer_test_' . uniqid();
         mkdir($this->testDir, 0777, true);
-        
+
         // Create mock SymfonyStyle
         $this->io = $this->createMock(SymfonyStyle::class);
-        
+
         $this->composerGenerator = new ComposerGenerator();
     }
 
@@ -37,7 +35,7 @@ class ComposerGeneratorTest extends TestCase
         if (!is_dir($dir)) {
             return;
         }
-        
+
         $items = scandir($dir);
         foreach ($items as $item) {
             if ($item != '.' && $item != '..') {
@@ -63,22 +61,22 @@ class ComposerGeneratorTest extends TestCase
 
         $this->assertIsString($content);
         $this->assertNotEmpty($content);
-        
+
         // Test JSON structure
         $jsonData = json_decode($content, true);
         $this->assertIsArray($jsonData);
         $this->assertEquals(JSON_ERROR_NONE, json_last_error());
-        
+
         // Test package information
         $this->assertEquals($packageName, $jsonData['name']);
         $this->assertEquals($description, $jsonData['description']);
-        
+
         // Test PSR-4 autoloading
         $this->assertArrayHasKey('autoload', $jsonData);
         $this->assertArrayHasKey('psr-4', $jsonData['autoload']);
         $this->assertArrayHasKey($namespace, $jsonData['autoload']['psr-4']);
         $this->assertEquals('src', $jsonData['autoload']['psr-4'][$namespace]);
-        
+
         // Test SixShop extension configuration
         $this->assertArrayHasKey('extra', $jsonData);
         $this->assertArrayHasKey('sixshop', $jsonData['extra']);
@@ -89,7 +87,7 @@ class ComposerGeneratorTest extends TestCase
     {
         $packageName = 'six-shop/test-extension';
         $content = '{"name": "six-shop/test-extension", "description": "Test"}';
-        
+
         $this->io->expects($this->once())
             ->method('success')
             ->with($this->stringContains('composer.json 文件已成功生成'));
@@ -106,7 +104,7 @@ class ComposerGeneratorTest extends TestCase
         $packageName = 'six-shop/test-extension';
         $content = '{"name": "six-shop/test-extension"}';
         $invalidDir = '/invalid/directory/path';
-        
+
         $this->io->expects($this->once())
             ->method('error')
             ->with($this->stringContains('无法创建目录'));
@@ -126,22 +124,27 @@ class ComposerGeneratorTest extends TestCase
         $this->io->expects($this->once())
             ->method('section')
             ->with('生成的 composer.json 内容:');
-            
+
         $this->io->expects($this->once())
             ->method('text')
             ->with($this->isType('string'));
-            
+
         $this->io->expects($this->once())
             ->method('confirm')
             ->with('是否将内容保存到 composer.json 文件?', true)
             ->willReturn(true);
-            
+
         $this->io->expects($this->once())
             ->method('success')
             ->with($this->stringContains('composer.json 文件已成功生成'));
 
         $result = $this->composerGenerator->createComposerJson(
-            $packageName, $namespace, $id, $description, $this->testDir, $this->io
+            $packageName,
+            $namespace,
+            $id,
+            $description,
+            $this->testDir,
+            $this->io
         );
 
         $this->assertTrue($result);
@@ -161,7 +164,12 @@ class ComposerGeneratorTest extends TestCase
             ->willReturn(false);
 
         $result = $this->composerGenerator->createComposerJson(
-            $packageName, $namespace, $id, $description, $this->testDir, $this->io
+            $packageName,
+            $namespace,
+            $id,
+            $description,
+            $this->testDir,
+            $this->io
         );
 
         $this->assertTrue($result);
@@ -178,7 +186,7 @@ class ComposerGeneratorTest extends TestCase
 
         foreach ($testCases as [$namespace, $packageName]) {
             $content = $this->composerGenerator->generateContent($packageName, $namespace, 'test-id', 'Test description');
-            
+
             $jsonData = json_decode($content, true);
             $this->assertArrayHasKey($namespace, $jsonData['autoload']['psr-4']);
             $this->assertEquals('src', $jsonData['autoload']['psr-4'][$namespace]);
@@ -195,7 +203,7 @@ class ComposerGeneratorTest extends TestCase
         );
 
         $jsonData = json_decode($content, true);
-        
+
         // Test required composer.json fields
         $this->assertArrayHasKey('name', $jsonData);
         $this->assertArrayHasKey('description', $jsonData);
@@ -203,10 +211,10 @@ class ComposerGeneratorTest extends TestCase
         $this->assertArrayHasKey('require', $jsonData);
         $this->assertArrayHasKey('autoload', $jsonData);
         $this->assertArrayHasKey('extra', $jsonData);
-        
+
         // Test type field
         $this->assertEquals('sixshop-extension', $jsonData['type']);
-        
+
         // Test minimum requirements
         $this->assertArrayHasKey('php', $jsonData['require']);
         $this->assertEquals('>=8.3', $jsonData['require']['php']);
@@ -227,7 +235,7 @@ class ComposerGeneratorTest extends TestCase
                 $expectedId,
                 'Test description'
             );
-            
+
             $jsonData = json_decode($content, true);
             $this->assertEquals($expectedId, $jsonData['extra']['sixshop']['id']);
         }
@@ -243,7 +251,7 @@ class ComposerGeneratorTest extends TestCase
             'test-id',
             'Test description'
         );
-        
+
         $this->assertIsString($content);
         $this->assertNotEmpty($content);
     }
@@ -253,7 +261,7 @@ class ComposerGeneratorTest extends TestCase
         $nestedDir = $this->testDir . '/nested/deep/directory';
         $packageName = 'six-shop/test-extension';
         $content = '{"name": "six-shop/test-extension"}';
-        
+
         $this->io->expects($this->once())
             ->method('success');
 
@@ -274,7 +282,7 @@ class ComposerGeneratorTest extends TestCase
         );
 
         $jsonData = json_decode($content, true);
-        
+
         // Verify complete structure
         $expectedStructure = [
             'name' => 'six-shop/advanced-extension',
@@ -304,4 +312,4 @@ class ComposerGeneratorTest extends TestCase
         $this->assertEquals($expectedStructure['autoload'], $jsonData['autoload']);
         $this->assertEquals($expectedStructure['extra'], $jsonData['extra']);
     }
-}
+}

+ 22 - 24
tests/Unit/Generator/ControllerGeneratorTest.php

@@ -5,8 +5,6 @@ namespace SixShop\MakerBundle\Tests\Unit\Generator;
 use PHPUnit\Framework\TestCase;
 use SixShop\MakerBundle\Generator\ControllerGenerator;
 use Symfony\Component\Console\Style\SymfonyStyle;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
 
 class ControllerGeneratorTest extends TestCase
 {
@@ -18,10 +16,10 @@ class ControllerGeneratorTest extends TestCase
     {
         $this->testDir = sys_get_temp_dir() . '/controller_test_' . uniqid();
         mkdir($this->testDir, 0777, true);
-        
+
         // Create mock SymfonyStyle
         $this->io = $this->createMock(SymfonyStyle::class);
-        
+
         $this->controllerGenerator = new ControllerGenerator();
     }
 
@@ -37,7 +35,7 @@ class ControllerGeneratorTest extends TestCase
         if (!is_dir($dir)) {
             return;
         }
-        
+
         $items = scandir($dir);
         foreach ($items as $item) {
             if ($item != '.' && $item != '..') {
@@ -82,24 +80,24 @@ class ControllerGeneratorTest extends TestCase
         $this->assertFileExists($controllerPath);
 
         $content = file_get_contents($controllerPath);
-        
+
         // Test namespace
         $this->assertStringContainsString('namespace TestExtension\Controller\Api;', $content);
-        
+
         // Test class name
         $this->assertStringContainsString('class UserController', $content);
-        
+
         // Test imports
         $this->assertStringContainsString('use SixShop\Core\Request;', $content);
         $this->assertStringContainsString('use TestExtension\Entity\UserEntity;', $content);
-        
+
         // Test methods
         $this->assertStringContainsString('public function index(Request $request)', $content);
         $this->assertStringContainsString('public function show(Request $request)', $content);
         $this->assertStringContainsString('public function store(Request $request)', $content);
         $this->assertStringContainsString('public function update(Request $request)', $content);
         $this->assertStringContainsString('public function destroy(Request $request)', $content);
-        
+
         // Test response helpers
         $this->assertStringContainsString('return page_response(', $content);
         $this->assertStringContainsString('return success_response(', $content);
@@ -135,16 +133,16 @@ class ControllerGeneratorTest extends TestCase
         $this->assertFileExists($controllerPath);
 
         $content = file_get_contents($controllerPath);
-        
+
         // Test namespace
         $this->assertStringContainsString('namespace TestExtension\Controller\Admin;', $content);
-        
+
         // Test class name
         $this->assertStringContainsString('class ProductController', $content);
-        
+
         // Test entity usage
         $this->assertStringContainsString('use TestExtension\Entity\ProductEntity;', $content);
-        
+
         // Test methods
         $this->assertStringContainsString('public function index(Request $request)', $content);
         $this->assertStringContainsString('public function show(Request $request)', $content);
@@ -162,7 +160,7 @@ class ControllerGeneratorTest extends TestCase
             'name' => 'test-extension'
         ];
         $fields = [
-            ['name' => 'id', 'type' => 'integer'], 
+            ['name' => 'id', 'type' => 'integer'],
             ['name' => 'name', 'type' => 'string']
         ];
 
@@ -216,7 +214,7 @@ class ControllerGeneratorTest extends TestCase
 
             $content = file_get_contents($controllerPath);
             $this->assertStringContainsString("class $expectedClass", $content);
-            
+
             // Clean up for next iteration
             unlink($controllerPath);
         }
@@ -251,7 +249,7 @@ class ControllerGeneratorTest extends TestCase
 
         $controllerPath = $this->testDir . '/src/Controller/Api/UserController.php';
         $content = file_get_contents($controllerPath);
-        
+
         // Test validation rules are present
         $this->assertStringContainsString('$rules = [', $content);
         $this->assertStringContainsString("'name' => 'required|string|max:255'", $content);
@@ -286,7 +284,7 @@ class ControllerGeneratorTest extends TestCase
 
         $controllerPath = $this->testDir . '/src/Controller/Api/PostController.php';
         $content = file_get_contents($controllerPath);
-        
+
         // Test entity method calls match expected patterns
         $this->assertStringContainsString('$entity->getList($page, $limit, $filters)', $content);
         $this->assertStringContainsString('$entity->getById($id)', $content);
@@ -300,7 +298,7 @@ class ControllerGeneratorTest extends TestCase
         $tableName = 'users';
         $tableComment = 'User table';
         $fields = [
-            ['name' => 'id', 'type' => 'integer'], 
+            ['name' => 'id', 'type' => 'integer'],
             ['name' => 'name', 'type' => 'string']
         ];
 
@@ -316,7 +314,7 @@ class ControllerGeneratorTest extends TestCase
                 'namespace' => $namespace,
                 'name' => 'test-extension'
             ];
-            
+
             $this->controllerGenerator->generateControllers(
                 $tableName,
                 $tableComment,
@@ -328,10 +326,10 @@ class ControllerGeneratorTest extends TestCase
 
             $controllerPath = $this->testDir . '/src/Controller/Api/UserController.php';
             $content = file_get_contents($controllerPath);
-            
+
             $this->assertStringContainsString("namespace $namespace\\Controller\\Api;", $content);
             $this->assertStringContainsString("use $namespace\\Entity\\UserEntity;", $content);
-            
+
             // Clean up for next iteration
             unlink($controllerPath);
         }
@@ -360,7 +358,7 @@ class ControllerGeneratorTest extends TestCase
                 $readOnlyDir,
                 $this->io
             );
-            
+
             // Should return false due to permission issues
             $this->assertFalse($result);
         } catch (\Exception $e) {
@@ -371,4 +369,4 @@ class ControllerGeneratorTest extends TestCase
             rmdir($readOnlyDir);
         }
     }
-}
+}

+ 47 - 46
tests/Unit/Generator/EntityGeneratorTest.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Tests\Unit\Generator;
@@ -25,14 +26,14 @@ class EntityGeneratorTest extends TestCase
         if (!is_dir($this->testDir)) {
             mkdir($this->testDir, 0755, true);
         }
-        
+
         $this->generator = new EntityGenerator();
         $this->extensionInfo = [
             'namespace' => 'SixShop\\Test\\',
             'packageName' => 'sixshop/test',
             'id' => 'sixshop-test'
         ];
-        
+
         // Create mock SymfonyStyle
         $input = new ArrayInput([]);
         $output = new BufferedOutput();
@@ -69,19 +70,19 @@ class EntityGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            $tableComment, 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            $tableComment,
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/TestUserEntity.php';
         $this->assertFileExists($entityFile);
-        
+
         $content = file_get_contents($entityFile);
         $this->assertStringContainsString('class TestUserEntity extends BaseEntity', $content);
         $this->assertStringContainsString('use SixShop\\Core\\Entity\\BaseEntity;', $content);
@@ -104,19 +105,19 @@ class EntityGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            $tableComment, 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            $tableComment,
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/ProductEntity.php';
         $content = file_get_contents($entityFile);
-        
+
         // Check method parameters
         $this->assertStringContainsString('public function indexProduct(array $params, array $pageAndLimit = []):Paginator| Collection', $content);
         $this->assertStringContainsString('public function createProduct(): array', $content);
@@ -139,19 +140,19 @@ class EntityGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            'Orders table', 
-            $fields, 
-            $customExtensionInfo, 
+            $tableName,
+            'Orders table',
+            $fields,
+            $customExtensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/OrderEntity.php';
         $this->assertFileExists($entityFile);
-        
+
         $content = file_get_contents($entityFile);
         $this->assertStringContainsString('namespace MyVendor\\CustomPackage\\Entity;', $content);
         $this->assertStringContainsString('use MyVendor\\CustomPackage\\Model\\OrderModel;', $content);
@@ -164,19 +165,19 @@ class EntityGeneratorTest extends TestCase
         $fields = [['name' => 'id', 'type' => 'biginteger']];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            'User profiles table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'User profiles table',
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/UserProfileEntity.php';
         $this->assertFileExists($entityFile);
-        
+
         $content = file_get_contents($entityFile);
         $this->assertStringContainsString('class UserProfileEntity extends BaseEntity', $content);
         $this->assertStringContainsString('public function indexUserProfile', $content);
@@ -190,21 +191,21 @@ class EntityGeneratorTest extends TestCase
 
         // Use a path that doesn't exist
         $newTestDir = $this->testDir . '/nonexistent/path';
-        
+
         $result = $this->generator->generateEntity(
-            $tableName, 
-            'Categories table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Categories table',
+            $fields,
+            $this->extensionInfo,
             $newTestDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $newTestDir . '/src/Entity/CategoryEntity.php';
         $this->assertFileExists($entityFile);
-        
+
         // Check that directories were created
         $this->assertTrue(is_dir($newTestDir . '/src/Entity'));
     }
@@ -218,19 +219,19 @@ class EntityGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            'Logs table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Logs table',
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/LogEntity.php';
         $content = file_get_contents($entityFile);
-        
+
         // Check implementation patterns
         $this->assertStringContainsString('$this->withSearch([', $content);
         $this->assertStringContainsString('$this->find($id)', $content);
@@ -245,19 +246,19 @@ class EntityGeneratorTest extends TestCase
         $fields = [['name' => 'id', 'type' => 'biginteger']];
 
         $result = $this->generator->generateEntity(
-            $tableName, 
-            'Settings table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Settings table',
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $entityFile = $this->testDir . '/src/Entity/SettingEntity.php';
         $content = file_get_contents($entityFile);
-        
+
         // Check required imports
         $this->assertStringContainsString('use SixShop\\Test\\Model\\SettingModel;', $content);
         $this->assertStringContainsString('use SixShop\\Core\\Entity\\BaseEntity;', $content);
@@ -265,4 +266,4 @@ class EntityGeneratorTest extends TestCase
         $this->assertStringContainsString('use think\\Paginator;', $content);
         $this->assertStringContainsString('use function SixShop\\Core\\throw_logic_exception;', $content);
     }
-}
+}

+ 29 - 28
tests/Unit/Generator/MigrationGeneratorTest.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Tests\Unit\Generator;
@@ -50,13 +51,13 @@ class MigrationGeneratorTest extends TestCase
     public function testGetSupportedFieldTypes(): void
     {
         $supportedTypes = $this->generator->getSupportedFieldTypes();
-        
+
         $expectedTypes = [
             'integer', 'biginteger', 'string', 'text', 'boolean',
             'decimal', 'float', 'datetime', 'timestamp', 'date',
             'time', 'binary', 'json'
         ];
-        
+
         foreach ($expectedTypes as $type) {
             $this->assertContains($type, $supportedTypes);
         }
@@ -91,17 +92,17 @@ class MigrationGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateMigration($tableName, $fields, 'create');
-        
+
         $this->assertTrue($result);
-        
+
         // Check if migration file was created
         $files = glob($this->testDir . '/*_create_test_users_table.php');
         $this->assertCount(1, $files);
-        
+
         // Check migration content
         $migrationFile = $files[0];
         $content = file_get_contents($migrationFile);
-        
+
         $this->assertStringContainsString('class CreateTestUsersTable', $content);
         $this->assertStringContainsString('$table = $this->table(\'test_users\')', $content);
         $this->assertStringContainsString('username', $content);
@@ -122,17 +123,17 @@ class MigrationGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateMigration($tableName, $fields, 'add_column');
-        
+
         $this->assertTrue($result);
-        
+
         // Check if migration file was created
         $files = glob($this->testDir . '/*_add_column_to_test_users_table.php');
         $this->assertCount(1, $files);
-        
+
         // Check migration content
         $migrationFile = $files[0];
         $content = file_get_contents($migrationFile);
-        
+
         $this->assertStringContainsString('class AddColumnToTestUsersTable', $content);
         $this->assertStringContainsString('phone', $content);
     }
@@ -145,17 +146,17 @@ class MigrationGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateMigration($tableName, $fields, 'drop_column');
-        
+
         $this->assertTrue($result);
-        
+
         // Check if migration file was created
         $files = glob($this->testDir . '/*_drop_column_from_test_users_table.php');
         $this->assertCount(1, $files);
-        
+
         // Check migration content
         $migrationFile = $files[0];
         $content = file_get_contents($migrationFile);
-        
+
         $this->assertStringContainsString('class DropColumnFromTestUsersTable', $content);
         $this->assertStringContainsString('deprecated_field', $content);
     }
@@ -175,16 +176,16 @@ class MigrationGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateMigration($tableName, $fields, 'create');
-        
+
         $this->assertTrue($result);
-        
+
         // Check migration content for decimal precision and scale
         $files = glob($this->testDir . '/*_create_test_products_table.php');
         $this->assertCount(1, $files);
-        
+
         $migrationFile = $files[0];
         $content = file_get_contents($migrationFile);
-        
+
         $this->assertStringContainsString('\'precision\' => 10', $content);
         $this->assertStringContainsString('\'scale\' => 2', $content);
     }
@@ -202,16 +203,16 @@ class MigrationGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateMigration($tableName, $fields, 'create');
-        
+
         $this->assertTrue($result);
-        
+
         // Check migration content for JSON field
         $files = glob($this->testDir . '/*_create_test_settings_table.php');
         $this->assertCount(1, $files);
-        
+
         $migrationFile = $files[0];
         $content = file_get_contents($migrationFile);
-        
+
         $this->assertStringContainsString('config_data', $content);
         $this->assertStringContainsString('json', $content);
     }
@@ -220,13 +221,13 @@ class MigrationGeneratorTest extends TestCase
     {
         $newPath = sys_get_temp_dir() . '/sixshop_new_path_' . uniqid();
         $generator = new MigrationGenerator($newPath);
-        
+
         $fields = [['name' => 'test_field', 'type' => 'string']];
         $result = $generator->generateMigration('test_table', $fields, 'create');
-        
+
         $this->assertTrue($result);
         $this->assertTrue(is_dir($newPath));
-        
+
         // Cleanup
         $this->removeDirectory($newPath);
     }
@@ -235,10 +236,10 @@ class MigrationGeneratorTest extends TestCase
     {
         $tableName = 'test_table';
         $fields = [['name' => 'test_field', 'type' => 'string']];
-        
+
         $result = $this->generator->generateMigration($tableName, $fields, 'invalid_action');
-        
+
         // Should handle gracefully and return false or throw exception
         $this->assertFalse($result);
     }
-}
+}

+ 43 - 42
tests/Unit/Generator/ModelGeneratorTest.php

@@ -1,4 +1,5 @@
 <?php
+
 declare(strict_types=1);
 
 namespace SixShop\MakerBundle\Tests\Unit\Generator;
@@ -25,14 +26,14 @@ class ModelGeneratorTest extends TestCase
         if (!is_dir($this->testDir)) {
             mkdir($this->testDir, 0755, true);
         }
-        
+
         $this->generator = new ModelGenerator();
         $this->extensionInfo = [
             'namespace' => 'SixShop\\Test\\',
             'packageName' => 'sixshop/test',
             'id' => 'sixshop-test'
         ];
-        
+
         // Create mock SymfonyStyle
         $input = new ArrayInput([]);
         $output = new BufferedOutput();
@@ -69,19 +70,19 @@ class ModelGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateModel(
-            $tableName, 
-            $tableComment, 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            $tableComment,
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $this->testDir . '/src/Model/TestUsersModel.php';
         $this->assertFileExists($modelFile);
-        
+
         $content = file_get_contents($modelFile);
         $this->assertStringContainsString('class TestUsersModel extends Model', $content);
         $this->assertStringContainsString('protected function getOptions(): array', $content);
@@ -103,27 +104,27 @@ class ModelGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateModel(
-            $tableName, 
-            $tableComment, 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            $tableComment,
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $this->testDir . '/src/Model/ProductModel.php';
         $this->assertFileExists($modelFile);
-        
+
         $content = file_get_contents($modelFile);
-        
+
         // Check field type mappings
         $this->assertStringContainsString("'name' => 'string'", $content);
         $this->assertStringContainsString("'price' => 'float'", $content);
         $this->assertStringContainsString("'is_active' => 'boolean'", $content);
         $this->assertStringContainsString("'config' => 'json'", $content);
-        
+
         // Check timestamp configuration
         $this->assertStringContainsString("'auto_timestamp' => true", $content);
         $this->assertStringContainsString("'create_time' => 'create_time'", $content);
@@ -142,19 +143,19 @@ class ModelGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateModel(
-            $tableName, 
-            'Orders table', 
-            $fields, 
-            $customExtensionInfo, 
+            $tableName,
+            'Orders table',
+            $fields,
+            $customExtensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $this->testDir . '/src/Model/OrderModel.php';
         $this->assertFileExists($modelFile);
-        
+
         $content = file_get_contents($modelFile);
         $this->assertStringContainsString('namespace MyVendor\\CustomPackage\\Model;', $content);
         $this->assertStringContainsString('class OrderModel extends Model', $content);
@@ -172,19 +173,19 @@ class ModelGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateModel(
-            $tableName, 
-            'Logs table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Logs table',
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $this->testDir . '/src/Model/LogModel.php';
         $content = file_get_contents($modelFile);
-        
+
         // Check that timestamp fields are mapped to '_time' suffix
         $this->assertStringContainsString("'create_time' => 'create_time'", $content);
         $this->assertStringContainsString("'update_time' => 'update_time'", $content);
@@ -198,21 +199,21 @@ class ModelGeneratorTest extends TestCase
 
         // Use a path that doesn't exist
         $newTestDir = $this->testDir . '/nonexistent/path';
-        
+
         $result = $this->generator->generateModel(
-            $tableName, 
-            'Categories table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Categories table',
+            $fields,
+            $this->extensionInfo,
             $newTestDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $newTestDir . '/src/Model/CategoryModel.php';
         $this->assertFileExists($modelFile);
-        
+
         // Check that directories were created
         $this->assertTrue(is_dir($newTestDir . '/src/Model'));
     }
@@ -228,22 +229,22 @@ class ModelGeneratorTest extends TestCase
         ];
 
         $result = $this->generator->generateModel(
-            $tableName, 
-            'Users table', 
-            $fields, 
-            $this->extensionInfo, 
+            $tableName,
+            'Users table',
+            $fields,
+            $this->extensionInfo,
             $this->testDir,
             $this->io
         );
 
         $this->assertTrue($result);
-        
+
         $modelFile = $this->testDir . '/src/Model/UserModel.php';
         $content = file_get_contents($modelFile);
-        
+
         // Check validation rules are generated
         $this->assertStringContainsString("'validate' => [", $content);
         $this->assertStringContainsString("'username' => 'require|max:50'", $content);
         $this->assertStringContainsString("'email' => 'require|email|max:255'", $content);
     }
-}
+}

+ 12 - 12
tests/Unit/Generator/RouteUpdaterTest.php

@@ -16,13 +16,13 @@ class RouteUpdaterTest extends TestCase
     {
         $this->testDir = sys_get_temp_dir() . '/route_test_' . uniqid();
         mkdir($this->testDir, 0777, true);
-        
+
         // Create route directory
         mkdir($this->testDir . '/route', 0777, true);
-        
+
         // Create mock SymfonyStyle
         $this->io = $this->createMock(SymfonyStyle::class);
-        
+
         $this->routeUpdater = new RouteUpdater();
     }
 
@@ -38,7 +38,7 @@ class RouteUpdaterTest extends TestCase
         if (!is_dir($dir)) {
             return;
         }
-        
+
         $items = scandir($dir);
         foreach ($items as $item) {
             if ($item != '.' && $item != '..') {
@@ -126,7 +126,7 @@ class RouteUpdaterTest extends TestCase
         // Create route files with existing routes
         $existingApiContent = "<?php\n// Existing routes\nRoute::resource('users', TestExtension\\Controller\\Api\\UserController::class);";
         $existingAdminContent = "<?php\n// Existing routes\nRoute::resource('users', TestExtension\\Controller\\Admin\\UserController::class);";
-        
+
         file_put_contents($this->testDir . '/route/api.php', $existingApiContent);
         file_put_contents($this->testDir . '/route/admin.php', $existingAdminContent);
 
@@ -157,7 +157,7 @@ class RouteUpdaterTest extends TestCase
         // Verify content wasn't duplicated
         $apiContent = file_get_contents($this->testDir . '/route/api.php');
         $this->assertEquals(1, substr_count($apiContent, 'Route::resource(\'users\''));
-        
+
         $adminContent = file_get_contents($this->testDir . '/route/admin.php');
         $this->assertEquals(1, substr_count($adminContent, 'Route::resource(\'users\''));
     }
@@ -176,7 +176,7 @@ class RouteUpdaterTest extends TestCase
         foreach ($testCases as $tableName => [$expectedController, $expectedResource]) {
             // Create fresh route files for each test
             file_put_contents($this->testDir . '/route/api.php', "<?php\n// API routes\n");
-            
+
             $extensionInfo = [
                 'namespace' => 'TestExtension',
                 'name' => 'test-extension'
@@ -202,7 +202,7 @@ class RouteUpdaterTest extends TestCase
 
         $tableName = 'posts';
         $tableComment = 'Post table';
-        
+
         // Test different namespace formats
         $namespaces = [
             'SimpleExtension',
@@ -229,7 +229,7 @@ class RouteUpdaterTest extends TestCase
 
             $adminContent = file_get_contents($this->testDir . '/route/admin.php');
             $this->assertStringContainsString("{$namespace}\\Controller\\Admin\\PostController::class", $adminContent);
-            
+
             // Reset files for next iteration
             file_put_contents($this->testDir . '/route/api.php', "<?php\n// API routes\n");
             file_put_contents($this->testDir . '/route/admin.php', "<?php\n// Admin routes\n");
@@ -304,7 +304,7 @@ class RouteUpdaterTest extends TestCase
     {
         $initialApiContent = "<?php\n// Existing API routes\nRoute::get('/test', 'TestController@test');";
         $initialAdminContent = "<?php\n// Existing Admin routes\nRoute::get('/admin-test', 'AdminTestController@test');";
-        
+
         file_put_contents($this->testDir . '/route/api.php', $initialApiContent);
         file_put_contents($this->testDir . '/route/admin.php', $initialAdminContent);
 
@@ -362,10 +362,10 @@ class RouteUpdaterTest extends TestCase
         }
 
         $apiContent = file_get_contents($this->testDir . '/route/api.php');
-        
+
         // Verify all resources are added
         $this->assertStringContainsString('Route::resource(\'users\', TestExtension\\Controller\\Api\\UserController::class);', $apiContent);
         $this->assertStringContainsString('Route::resource(\'posts\', TestExtension\\Controller\\Api\\PostController::class);', $apiContent);
         $this->assertStringContainsString('Route::resource(\'categories\', TestExtension\\Controller\\Api\\CategoryController::class);', $apiContent);
     }
-}
+}

+ 68 - 68
tests/manual_test.php

@@ -1,7 +1,8 @@
 <?php
+
 /**
  * Manual Test Script for SixShop Maker Bundle
- * 
+ *
  * This script performs comprehensive tests of all functionality
  * Run: php tests/manual_test.php
  */
@@ -21,7 +22,6 @@ use SixShop\MakerBundle\Generator\ModelGenerator;
 use SixShop\MakerBundle\Generator\EntityGenerator;
 use SixShop\MakerBundle\Generator\ControllerGenerator;
 use SixShop\MakerBundle\Generator\ComposerGenerator;
-use SixShop\MakerBundle\Generator\RouteUpdater;
 
 echo "🧪 SixShop Maker Bundle - Comprehensive Test Suite\n";
 echo "=" . str_repeat("=", 50) . "\n\n";
@@ -34,7 +34,7 @@ function test(string $name, callable $test): void
 {
     global $passed, $failed;
     echo "📋 Testing: {$name}... ";
-    
+
     try {
         $result = $test();
         if ($result) {
@@ -67,7 +67,7 @@ function cleanupTestDir(): void
             new RecursiveDirectoryIterator($testDir, RecursiveDirectoryIterator::SKIP_DOTS),
             RecursiveIteratorIterator::CHILD_FIRST
         );
-        
+
         foreach ($iterator as $file) {
             if ($file->isDir()) {
                 rmdir($file->getRealPath());
@@ -80,179 +80,179 @@ function cleanupTestDir(): void
 }
 
 // Test 1: MigrationGenerator
-test("MigrationGenerator - Create Table", function() {
+test("MigrationGenerator - Create Table", function () {
     $testDir = setupTestDir();
     $generator = new MigrationGenerator($testDir . '/migrations');
-    
+
     $fields = [
         ['name' => 'id', 'type' => 'biginteger', 'null' => false],
         ['name' => 'name', 'type' => 'string', 'length' => 100, 'null' => false],
         ['name' => 'email', 'type' => 'string', 'length' => 255, 'null' => false, 'unique' => true]
     ];
-    
+
     $result = $generator->generateMigration('users', $fields, 'create');
-    
+
     // Check if file was created
     $files = glob($testDir . '/migrations/*_create_users_table.php');
     return $result && count($files) === 1;
 });
 
-test("MigrationGenerator - Add Column", function() {
+test("MigrationGenerator - Add Column", function () {
     $testDir = setupTestDir();
     $generator = new MigrationGenerator($testDir . '/migrations');
-    
+
     $fields = [
         ['name' => 'phone', 'type' => 'string', 'length' => 20, 'null' => true]
     ];
-    
+
     $result = $generator->generateMigration('users', $fields, 'add_column');
-    
+
     $files = glob($testDir . '/migrations/*_add_column_to_users_table.php');
     return $result && count($files) === 1;
 });
 
-test("MigrationGenerator - Drop Column", function() {
+test("MigrationGenerator - Drop Column", function () {
     $testDir = setupTestDir();
     $generator = new MigrationGenerator($testDir . '/migrations');
-    
+
     $fields = [
         ['name' => 'deprecated_field']
     ];
-    
+
     $result = $generator->generateMigration('users', $fields, 'drop_column');
-    
+
     $files = glob($testDir . '/migrations/*_drop_column_from_users_table.php');
     return $result && count($files) === 1;
 });
 
 // Test 2: ComposerGenerator
-test("ComposerGenerator - Package Name Validation", function() {
+test("ComposerGenerator - Package Name Validation", function () {
     $generator = new ComposerGenerator();
-    
+
     $validNames = ['vendor/package', 'sixshop/hello-world'];
     $invalidNames = ['invalid', 'vendor/', '/package'];
-    
+
     foreach ($validNames as $name) {
         if (!$generator->validatePackageName($name)) {
             return false;
         }
     }
-    
+
     foreach ($invalidNames as $name) {
         if ($generator->validatePackageName($name)) {
             return false;
         }
     }
-    
+
     return true;
 });
 
-test("ComposerGenerator - Namespace Conversion", function() {
+test("ComposerGenerator - Namespace Conversion", function () {
     $generator = new ComposerGenerator();
-    
+
     $testCases = [
         'sixshop/hello-world' => 'SixShop\\HelloWorld\\',
         'vendor/simple' => 'Vendor\\Simple\\',
         'my-vendor/complex-name' => 'MyVendor\\ComplexName\\'
     ];
-    
+
     foreach ($testCases as $package => $expected) {
         $result = $generator->convertPackageNameToNamespace($package);
         if ($result !== $expected) {
             return false;
         }
     }
-    
+
     return true;
 });
 
-test("ComposerGenerator - Generate composer.json", function() {
+test("ComposerGenerator - Generate composer.json", function () {
     $testDir = setupTestDir();
     $generator = new ComposerGenerator();
-    
+
     $packageInfo = [
         'packageName' => 'sixshop/test-extension',
         'description' => 'A test extension',
         'namespace' => 'SixShop\\TestExtension\\',
         'id' => 'sixshop-test-extension'
     ];
-    
+
     $result = $generator->generateComposerJson($packageInfo, $testDir);
-    
+
     return $result && file_exists($testDir . '/composer.json');
 });
 
 // Test 3: ModelGenerator
-test("ModelGenerator - Generate Model", function() {
+test("ModelGenerator - Generate Model", function () {
     $testDir = setupTestDir();
     $generator = new ModelGenerator();
-    
+
     $fields = [
         ['name' => 'id', 'type' => 'biginteger'],
         ['name' => 'name', 'type' => 'string', 'length' => 100],
         ['name' => 'price', 'type' => 'decimal', 'precision' => 10, 'scale' => 2]
     ];
-    
+
     $extensionInfo = [
         'namespace' => 'SixShop\\Test\\',
         'packageName' => 'sixshop/test',
         'id' => 'sixshop-test'
     ];
-    
+
     $result = $generator->generateModel('products', 'Products table', $fields, $extensionInfo, $testDir);
-    
+
     return $result && file_exists($testDir . '/src/Model/ProductModel.php');
 });
 
-// Test 4: EntityGenerator  
-test("EntityGenerator - Generate Entity", function() {
+// Test 4: EntityGenerator
+test("EntityGenerator - Generate Entity", function () {
     $testDir = setupTestDir();
     $generator = new EntityGenerator();
-    
+
     $fields = [
         ['name' => 'id', 'type' => 'biginteger'],
         ['name' => 'name', 'type' => 'string']
     ];
-    
+
     $extensionInfo = [
         'namespace' => 'SixShop\\Test\\',
         'packageName' => 'sixshop/test',
         'id' => 'sixshop-test'
     ];
-    
+
     $result = $generator->generateEntity('products', 'Products table', $fields, $extensionInfo, $testDir);
-    
+
     return $result && file_exists($testDir . '/src/Entity/ProductEntity.php');
 });
 
 // Test 5: ControllerGenerator
-test("ControllerGenerator - Generate Controllers", function() {
+test("ControllerGenerator - Generate Controllers", function () {
     $testDir = setupTestDir();
     $generator = new ControllerGenerator();
-    
+
     $fields = [
         ['name' => 'id', 'type' => 'biginteger'],
         ['name' => 'name', 'type' => 'string']
     ];
-    
+
     $extensionInfo = [
         'namespace' => 'SixShop\\Test\\',
         'packageName' => 'sixshop/test',
         'id' => 'sixshop-test'
     ];
-    
+
     $result = $generator->generateControllers('products', 'Products table', $fields, $extensionInfo, $testDir);
-    
+
     $adminExists = file_exists($testDir . '/src/Controller/Admin/ProductController.php');
     $apiExists = file_exists($testDir . '/src/Controller/Api/ProductController.php');
-    
+
     return $result && $adminExists && $apiExists;
 });
 
 // Test 6: Complete Workflow
-test("Complete Workflow - All Components", function() {
+test("Complete Workflow - All Components", function () {
     $testDir = setupTestDir();
-    
+
     $tableName = 'orders';
     $tableComment = 'Orders management';
     $fields = [
@@ -261,64 +261,64 @@ test("Complete Workflow - All Components", function() {
         ['name' => 'total', 'type' => 'decimal', 'precision' => 10, 'scale' => 2],
         ['name' => 'status', 'type' => 'string', 'length' => 20, 'default' => 'pending']
     ];
-    
+
     $extensionInfo = [
         'namespace' => 'SixShop\\Shop\\',
         'packageName' => 'sixshop/shop',
         'id' => 'sixshop-shop'
     ];
-    
+
     // Generate migration
     $migrationGenerator = new MigrationGenerator($testDir . '/database/migrations');
     $migrationResult = $migrationGenerator->generateMigration($tableName, $fields, 'create');
-    
+
     // Generate model
     $modelGenerator = new ModelGenerator();
     $modelResult = $modelGenerator->generateModel($tableName, $tableComment, $fields, $extensionInfo, $testDir);
-    
+
     // Generate entity
     $entityGenerator = new EntityGenerator();
     $entityResult = $entityGenerator->generateEntity($tableName, $tableComment, $fields, $extensionInfo, $testDir);
-    
+
     // Generate controllers
     $controllerGenerator = new ControllerGenerator();
     $controllerResult = $controllerGenerator->generateControllers($tableName, $tableComment, $fields, $extensionInfo, $testDir);
-    
+
     // Verify all files exist
     $migrationExists = !empty(glob($testDir . '/database/migrations/*_create_orders_table.php'));
     $modelExists = file_exists($testDir . '/src/Model/OrderModel.php');
     $entityExists = file_exists($testDir . '/src/Entity/OrderEntity.php');
     $adminControllerExists = file_exists($testDir . '/src/Controller/Admin/OrderController.php');
     $apiControllerExists = file_exists($testDir . '/src/Controller/Api/OrderController.php');
-    
+
     return $migrationResult && $modelResult && $entityResult && $controllerResult &&
            $migrationExists && $modelExists && $entityExists && $adminControllerExists && $apiControllerExists;
 });
 
 // Test 7: Field Type Handling
-test("Field Type Support", function() {
+test("Field Type Support", function () {
     $generator = new MigrationGenerator();
     $supportedTypes = $generator->getSupportedFieldTypes();
-    
+
     $expectedTypes = [
         'integer', 'biginteger', 'string', 'text', 'boolean',
         'decimal', 'float', 'datetime', 'timestamp', 'date',
         'time', 'binary', 'json'
     ];
-    
+
     foreach ($expectedTypes as $type) {
         if (!in_array($type, $supportedTypes)) {
             return false;
         }
     }
-    
+
     return true;
 });
 
 // Test 8: Template Content Validation
-test("Template Content Quality", function() {
+test("Template Content Quality", function () {
     $testDir = setupTestDir();
-    
+
     // Generate complete set
     $generator = new ModelGenerator();
     $extensionInfo = [
@@ -326,15 +326,15 @@ test("Template Content Quality", function() {
         'packageName' => 'sixshop/test',
         'id' => 'sixshop-test'
     ];
-    
+
     $fields = [
         ['name' => 'name', 'type' => 'string', 'length' => 100]
     ];
-    
+
     $generator->generateModel('items', 'Test items', $fields, $extensionInfo, $testDir);
-    
+
     $content = file_get_contents($testDir . '/src/Model/ItemModel.php');
-    
+
     // Check for required elements
     $checks = [
         'class ItemModel extends Model',
@@ -343,13 +343,13 @@ test("Template Content Quality", function() {
         "'pk' => 'id'",
         "'auto_timestamp' => true"
     ];
-    
+
     foreach ($checks as $check) {
         if (strpos($content, $check) === false) {
             return false;
         }
     }
-    
+
     return true;
 });
 
@@ -374,4 +374,4 @@ echo "- Comprehensive demo: php test/comprehensive_demo.php\n";
 // Cleanup
 cleanupTestDir();
 
-echo "\n🧹 Cleanup completed.\n";
+echo "\n🧹 Cleanup completed.\n";

+ 30 - 29
tests/simple_test.php

@@ -1,4 +1,5 @@
 <?php
+
 /**
  * Simplified Test Script for SixShop Maker Bundle
  * Tests the actual working functionality
@@ -27,7 +28,7 @@ function test(string $name, callable $test): void
 {
     global $passed, $failed;
     echo "📋 Testing: {$name}... ";
-    
+
     try {
         $result = $test();
         if ($result) {
@@ -60,7 +61,7 @@ function cleanupTestDir(): void
             new RecursiveDirectoryIterator($testDir, RecursiveDirectoryIterator::SKIP_DOTS),
             RecursiveIteratorIterator::CHILD_FIRST
         );
-        
+
         foreach ($iterator as $file) {
             if ($file->isDir()) {
                 rmdir($file->getRealPath());
@@ -73,46 +74,46 @@ function cleanupTestDir(): void
 }
 
 // Test 1: Basic MigrationGenerator functionality
-test("MigrationGenerator - Create Table", function() {
+test("MigrationGenerator - Create Table", function () {
     $testDir = setupTestDir();
     $generator = new MigrationGenerator($testDir . '/migrations');
-    
+
     $fields = [
         ['name' => 'id', 'type' => 'biginteger', 'null' => false],
         ['name' => 'name', 'type' => 'string', 'length' => 100, 'null' => false],
         ['name' => 'email', 'type' => 'string', 'length' => 255, 'null' => false, 'unique' => true]
     ];
-    
+
     $result = $generator->generateMigration('users', $fields, 'create');
-    
+
     if (!$result) {
         return false;
     }
-    
+
     // Check if file was created
     $files = glob($testDir . '/migrations/*_create_users_table.php');
     return count($files) === 1;
 });
 
-test("MigrationGenerator - Supported Field Types", function() {
+test("MigrationGenerator - Supported Field Types", function () {
     $generator = new MigrationGenerator();
     $supportedTypes = $generator->getSupportedFieldTypes();
-    
+
     $requiredTypes = ['string', 'integer', 'biginteger', 'text', 'boolean', 'decimal', 'json', 'timestamp'];
-    
+
     foreach ($requiredTypes as $type) {
         if (!in_array($type, $supportedTypes)) {
             return false;
         }
     }
-    
+
     return true;
 });
 
-test("MigrationGenerator - Complex Field Options", function() {
+test("MigrationGenerator - Complex Field Options", function () {
     $testDir = setupTestDir();
     $generator = new MigrationGenerator($testDir . '/migrations');
-    
+
     $fields = [
         [
             'name' => 'price',
@@ -129,36 +130,36 @@ test("MigrationGenerator - Complex Field Options", function() {
             'comment' => 'Configuration data'
         ]
     ];
-    
+
     $result = $generator->generateMigration('products', $fields, 'create');
-    
+
     if (!$result) {
         return false;
     }
-    
+
     // Check if file was created and contains the expected content
     $files = glob($testDir . '/migrations/*_create_products_table.php');
     if (count($files) !== 1) {
         return false;
     }
-    
+
     $content = file_get_contents($files[0]);
-    return strpos($content, 'decimal') !== false && 
+    return strpos($content, 'decimal') !== false &&
            strpos($content, 'json') !== false &&
            strpos($content, 'precision') !== false;
 });
 
-test("CLI Command Availability", function() {
+test("CLI Command Availability", function () {
     $binPath = __DIR__ . '/../bin/sixshop-maker';
     if (!file_exists($binPath)) {
         return false;
     }
-    
+
     // Check if file is executable
     return is_executable($binPath);
 });
 
-test("Template Files Exist", function() {
+test("Template Files Exist", function () {
     $requiredTemplates = [
         'composer.json.tpl.php',
         'config.php.tpl.php',
@@ -168,33 +169,33 @@ test("Template Files Exist", function() {
         'src/Entity/Entity.php.tpl.php',
         'src/Model/Model.php.tpl.php'
     ];
-    
+
     $templateDir = __DIR__ . '/../templates/';
-    
+
     foreach ($requiredTemplates as $template) {
         if (!file_exists($templateDir . $template)) {
             return false;
         }
     }
-    
+
     return true;
 });
 
-test("Documentation Files", function() {
+test("Documentation Files", function () {
     $requiredFiles = [
         'README.md',
         'LICENSE',
         'composer.json'
     ];
-    
+
     $rootDir = __DIR__ . '/../';
-    
+
     foreach ($requiredFiles as $file) {
         if (!file_exists($rootDir . $file)) {
             return false;
         }
     }
-    
+
     return true;
 });
 
@@ -221,4 +222,4 @@ echo "- List commands: php bin/sixshop-maker list\n";
 // Cleanup
 cleanupTestDir();
 
-echo "\n🧹 Test cleanup completed.\n";
+echo "\n🧹 Test cleanup completed.\n";