| 1: | <?php |
| 2: | namespace TIC\MakeBundle\Maker; |
| 3: | |
| 4: | use Symfony\Bundle\MakerBundle\Maker\AbstractMaker; |
| 5: | use Symfony\Bundle\MakerBundle\DependencyBuilder; |
| 6: | use Symfony\Bundle\MakerBundle\ConsoleStyle; |
| 7: | use Symfony\Bundle\MakerBundle\Generator; |
| 8: | use Symfony\Bundle\MakerBundle\FileManager; |
| 9: | use Symfony\Bundle\MakerBundle\Str; |
| 10: | use Symfony\Bundle\MakerBundle\Util\ClassNameDetails; |
| 11: | use Symfony\Bundle\MakerBundle\Doctrine\EntityDetails; |
| 12: | use Symfony\Bundle\MakerBundle\InputConfiguration; |
| 13: | use Symfony\Component\Console\Input\InputInterface; |
| 14: | use Symfony\Component\Console\Input\InputOption; |
| 15: | use Symfony\Component\Console\Command\Command; |
| 16: | |
| 17: | use TIC\CoreBundle\Util\StringHelper; |
| 18: | use TIC\MakeBundle\Helper\GeneratorTwigHelper; |
| 19: | use TIC\MakeBundle\Renderer\FormTypeRenderer; |
| 20: | |
| 21: | |
| 22: | |
| 23: | |
| 24: | abstract class BaseMaker extends AbstractMaker |
| 25: | { |
| 26: | |
| 27: | protected $makerName; |
| 28: | protected static $description; |
| 29: | protected static $skelPath = __DIR__.'/../Resources/skeleton/'; |
| 30: | protected $dependencies = []; |
| 31: | protected $twig = true; |
| 32: | protected $test = false; |
| 33: | protected $over = false; |
| 34: | protected $ask = true; |
| 35: | protected $io; |
| 36: | protected $generator; |
| 37: | protected $fileManager; |
| 38: | |
| 39: | public function __construct() |
| 40: | { |
| 41: | $this->makerName = self::getMakerName(); |
| 42: | } |
| 43: | |
| 44: | |
| 45: | |
| 46: | |
| 47: | public function setFileManager(FileManager $fileManager) |
| 48: | { |
| 49: | $this->fileManager = $fileManager; |
| 50: | } |
| 51: | |
| 52: | |
| 53: | |
| 54: | |
| 55: | protected static function getMakerName(): string |
| 56: | { |
| 57: | if (preg_match("/\\\\Maker\\\\Make(.*)\$/", static::class, $m)) return $m[1]; |
| 58: | } |
| 59: | |
| 60: | |
| 61: | |
| 62: | |
| 63: | |
| 64: | public static function getCommandName(): string |
| 65: | { |
| 66: | |
| 67: | return 'tic:make:' . strtolower(self::getMakerName()); |
| 68: | } |
| 69: | |
| 70: | |
| 71: | |
| 72: | |
| 73: | public static function getCommandDescription(): string |
| 74: | { |
| 75: | return sprintf("Génération basée sur les TIC Bundles pour %s.", self::getMakerName()); |
| 76: | } |
| 77: | |
| 78: | |
| 79: | |
| 80: | |
| 81: | |
| 82: | |
| 83: | |
| 84: | public function configureCommand(Command $command, InputConfiguration $inputConfig) |
| 85: | { |
| 86: | $command |
| 87: | ->addOption('test', 't', InputOption::VALUE_NONE, "Mode test: affichage sans génération de fichier") |
| 88: | ->addOption('no-template', null, InputOption::VALUE_NONE, "Désactiver la génération de template twig") |
| 89: | ->addOption('overwrite', null, InputOption::VALUE_NONE, "ATTENTION: suppression des fichiers déjà existants !") |
| 90: | ->addOption('yes', null, InputOption::VALUE_NONE, "Désactiver les demandes de confirmation") |
| 91: | ; |
| 92: | $helpFile = __DIR__ . sprintf('/../Resources/help/Make%s.txt', $this->makerName); |
| 93: | if (file_exists($helpFile)) $command->setHelp(file_get_contents($helpFile)); |
| 94: | |
| 95: | $this->ticConfigure($command); |
| 96: | } |
| 97: | |
| 98: | |
| 99: | |
| 100: | |
| 101: | public function configureDependencies(DependencyBuilder $dependencies) |
| 102: | { |
| 103: | foreach ($this->dependencies as $dependency) $dependencies->addClassDependency( |
| 104: | $dependency['class'], $dependency['value'] |
| 105: | ); |
| 106: | } |
| 107: | |
| 108: | |
| 109: | |
| 110: | |
| 111: | public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator) |
| 112: | { |
| 113: | $this->generator = $generator; |
| 114: | $this->io = $io; |
| 115: | $this->ask = ! $input->getOption('yes'); |
| 116: | $this->test = $input->getOption('test'); |
| 117: | $this->over = $input->getOption('overwrite'); |
| 118: | $this->twig = ! $input->getOption('no-template'); |
| 119: | |
| 120: | $rc = $this->ticGenerate($input, $io); |
| 121: | if (! $rc) return; |
| 122: | |
| 123: | $generator->writeChanges(); |
| 124: | $this->writeSuccessMessage($io); |
| 125: | $io->text("Next: Open your new controller class and add some pages!"); |
| 126: | } |
| 127: | |
| 128: | |
| 129: | |
| 130: | |
| 131: | |
| 132: | abstract protected function ticConfigure(Command $command); |
| 133: | |
| 134: | |
| 135: | |
| 136: | |
| 137: | abstract protected function ticGenerate(InputInterface $input); |
| 138: | |
| 139: | |
| 140: | |
| 141: | |
| 142: | |
| 143: | |
| 144: | |
| 145: | protected function fixClassName(string $name, ?bool $skip_path = false): string |
| 146: | { |
| 147: | $name = StringHelper::asciify($name); |
| 148: | $name = preg_replace_callback('/_(.)/', function($m){ return strtoupper($m[1]); }, $name); |
| 149: | |
| 150: | if (strpos("\\", $name) === FALSE) $name = preg_replace('/[\/\.]+/', "\\", $name); |
| 151: | |
| 152: | $parts = explode("\\", $name); |
| 153: | return ($skip_path) ? ucfirst(array_pop($parts)) : implode("\\", array_map('ucfirst', $parts)); |
| 154: | } |
| 155: | |
| 156: | |
| 157: | |
| 158: | |
| 159: | protected function getRootNamespace(ClassNameDetails $classNameDetails, ?string $classFolder = null): string |
| 160: | { |
| 161: | $ns = str_replace($classNameDetails->getRelativeName(), '', $classNameDetails->getFullName()); |
| 162: | $ns = ($classFolder === null) ? preg_replace("/\\\\[^\\\\]*\\\\?\$/", "", $ns) : str_replace("\\".$classFolder."\\", "", $ns); |
| 163: | return $ns; |
| 164: | } |
| 165: | |
| 166: | |
| 167: | |
| 168: | |
| 169: | protected function getSnakeCaseName(ClassNameDetails $classNameDetails): string |
| 170: | { |
| 171: | $parts = explode("\\", $classNameDetails->getRelativeNameWithoutSuffix()); |
| 172: | $last = array_pop($parts); |
| 173: | if (empty($parts)) $parts[] = 'app'; |
| 174: | return strtolower(implode('', $parts) . '_' . $last); |
| 175: | } |
| 176: | |
| 177: | |
| 178: | |
| 179: | |
| 180: | protected function getVariablesFromName(string $inputName, ?string $type = "Controller"): array |
| 181: | { |
| 182: | $classPrefix = ucfirst($type) . "\\"; |
| 183: | $classSuffix = ($classPrefix === "Entity\\") ? "" : ucfirst($type); |
| 184: | $classDetails = $this->generator->createClassNameDetails($inputName, $classPrefix, $classSuffix); |
| 185: | $nameSpace = $this->getRootNamespace($classDetails); |
| 186: | $snakeCase = $this->getSnakeCaseName($classDetails); |
| 187: | |
| 188: | $variables = array( |
| 189: | 'base_name' => $classDetails->getRelativeNameWithoutSuffix(), |
| 190: | 'item_name' => preg_replace("/${classSuffix}\$/", '', $classDetails->getShortName()), |
| 191: | 'trans_base' => str_replace('_', '.', $snakeCase) . '.', |
| 192: | ); |
| 193: | if ($classPrefix == "Controller\\") $variables+= array( |
| 194: | 'route_name' => $snakeCase . '_', |
| 195: | 'route_path' => Str::asRoutePath($variables['base_name']), |
| 196: | 'views_path' => Str::asFilePath($variables['base_name']), |
| 197: | 'views_root' => preg_replace('/^(\/*)([^\/]+\/)?(.+)$/', '${2}layout.html.twig', Str::asFilePath($variables['base_name'])), |
| 198: | 'ctrl_class' => $classDetails->getFullName(), |
| 199: | 'form_class' => $nameSpace . "\\Form\\" . $variables['base_name'] . "Type", |
| 200: | ); |
| 201: | if ($classPrefix == "Type\\") $variables+= array( |
| 202: | 'form_class' => $nameSpace . "\\Form\\" . $variables['base_name'] . "Type", |
| 203: | ); |
| 204: | return $variables+= array( |
| 205: | 'item_class' => $nameSpace . "\\Entity\\" . $variables['item_name'], |
| 206: | 'repo_class' => $nameSpace . "\\Repository\\" . $variables['item_name'] . "Repository", |
| 207: | 'root_directory' => $this->generator->getRootDirectory(), |
| 208: | ); |
| 209: | } |
| 210: | |
| 211: | |
| 212: | |
| 213: | protected function ticGenerateTemplates(string $skelDir, array $templates, array $variables = []): array |
| 214: | { |
| 215: | if (isset($variables['views_root'])) $this->initTwigLayout($variables['views_root'], $variables); |
| 216: | |
| 217: | $res = array(); |
| 218: | foreach ($templates as $template) $res[] = $this->ticGenerateTemplate( |
| 219: | $variables['views_path'] . '/' . $template . '.html.twig', |
| 220: | self::$skelPath . $skelDir . '/twig_' . $template . '.tpl.php', |
| 221: | $variables |
| 222: | ); |
| 223: | return $res; |
| 224: | } |
| 225: | |
| 226: | |
| 227: | |
| 228: | |
| 229: | protected function initTwigLayout(string $tpl_path, array $variables = []): void |
| 230: | { |
| 231: | $src_path = static::$skelPath . 'base/twig_layout.tpl.php'; |
| 232: | |
| 233: | |
| 234: | try { $this->generator->generateTemplate($tpl_path, $src_path, $variables); } |
| 235: | catch (\Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException $e) { } |
| 236: | } |
| 237: | |
| 238: | protected function ticGenerateTemplate(string $targetPath, string $templateName, array $variables = []): ?string |
| 239: | { |
| 240: | $templatePath = $this->fileManager->getPathForTemplate($targetPath); |
| 241: | if (! $this->checkTargetPath( $templatePath )) return null; |
| 242: | |
| 243: | $variables['ticHelper'] = new GeneratorTwigHelper(); |
| 244: | $this->generator->generateTemplate($targetPath, $templateName, $variables); |
| 245: | return $templatePath; |
| 246: | } |
| 247: | |
| 248: | protected function ticGenerateClass(string $className, string $templateName, array $variables = []): ?string |
| 249: | { |
| 250: | if (! $this->checkTargetPath( $this->fileManager->getRelativePathForFutureClass($className) )) return null; |
| 251: | |
| 252: | return $this->generator->generateClass($className, $templateName, $variables); |
| 253: | } |
| 254: | |
| 255: | protected function ticGenerateController(string $controllerClassName, string $controllerTemplatePath, array $parameters = []): ?string |
| 256: | { |
| 257: | if (! $this->checkTargetPath( $this->fileManager->getRelativePathForFutureClass($controllerClassName) )) return null; |
| 258: | |
| 259: | return $this->generator->generateController($controllerClassName, $controllerTemplatePath, $parameters); |
| 260: | } |
| 261: | |
| 262: | protected function ticGenerateFormType(FormTypeRenderer $renderer, ClassNameDetails $formDetails, EntityDetails $doctrineDetails, ClassNameDetails $entityDetails, array $constraintClasses = [], array $extraUseClasses = []): ?string |
| 263: | { |
| 264: | if (! $this->checkTargetPath( $this->fileManager->getRelativePathForFutureClass($formDetails->getFullName()) )) return null; |
| 265: | |
| 266: | return $renderer->generate($formDetails, $doctrineDetails, $entityDetails); |
| 267: | } |
| 268: | |
| 269: | protected function checkTargetPath(string $targetPath): bool |
| 270: | { |
| 271: | $fullPath = $this->fileManager->absolutizePath($targetPath); |
| 272: | |
| 273: | if (! file_exists($fullPath)) return true; |
| 274: | $this->io->caution("Le fichier à générer existe déjà : " . $fullPath); |
| 275: | |
| 276: | if (! $this->over) return false; |
| 277: | if (! $this->confirm("Confirmer la suppression du fichier actuel")) return false; |
| 278: | |
| 279: | return unlink($fullPath); |
| 280: | } |
| 281: | |
| 282: | protected function confirm(string $question): bool |
| 283: | { |
| 284: | if (! $this->ask) return true; |
| 285: | return $this->io->confirm($question, true); |
| 286: | } |
| 287: | |
| 288: | |
| 289: | |
| 290: | |
| 291: | |
| 292: | protected static function hash2list(array $hash): array |
| 293: | { |
| 294: | $lines = array(); |
| 295: | foreach ($hash as $k => $v) $lines[] = array($k, $v); |
| 296: | return $lines; |
| 297: | } |
| 298: | |
| 299: | |
| 300: | |
| 301: | |
| 302: | protected static function indentFile(string $file, ?bool $with_space = false): bool |
| 303: | { |
| 304: | if (! file_exists($file)) return false; |
| 305: | $old = ($with_space) ? "\t" : " "; |
| 306: | $new = ($with_space) ? " " : "\t"; |
| 307: | $len = strlen($old); |
| 308: | $data = file_get_contents($file); |
| 309: | $data = preg_replace_callback("/\n(($old)+)/", function($m) use($new,$len) { return "\n" . str_repeat($new, ceil(strlen($m[1]) / $len)); }, $data); |
| 310: | $data = str_replace(";\n\n$new$new", ";\n$new$new", $data); |
| 311: | file_put_contents($file, $data); |
| 312: | return true; |
| 313: | } |
| 314: | |
| 315: | |
| 316: | |
| 317: | |
| 318: | protected function summary(array $results): bool |
| 319: | { |
| 320: | $this->io->section("Liste des fichiers générés :"); |
| 321: | $this->io->table([ 'Name', 'Path' ], $results); |
| 322: | return true; |
| 323: | } |
| 324: | |
| 325: | } |
| 326: | |