Victoire /
victoire
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Victoire\Bundle\WidgetBundle\Command; |
||
| 4 | |||
| 5 | use Doctrine\DBAL\Types\Type; |
||
| 6 | use Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand; |
||
| 7 | use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; |
||
| 8 | use Sensio\Bundle\GeneratorBundle\Command\Validators; |
||
| 9 | use Sensio\Bundle\GeneratorBundle\Generator\DoctrineEntityGenerator; |
||
| 10 | use Symfony\Component\Console\Input\InputInterface; |
||
| 11 | use Symfony\Component\Console\Input\InputOption; |
||
| 12 | use Symfony\Component\Console\Output\OutputInterface; |
||
| 13 | use Symfony\Component\Console\Question\ConfirmationQuestion; |
||
| 14 | use Symfony\Component\Console\Question\Question; |
||
| 15 | use Symfony\Component\DependencyInjection\Container; |
||
| 16 | use Symfony\Component\HttpKernel\Bundle\BundleInterface; |
||
| 17 | use Victoire\Bundle\WidgetBundle\Generator\WidgetGenerator; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Create a new Widget for VictoireCMS. |
||
| 21 | */ |
||
| 22 | class CreateWidgetCommand extends GenerateBundleCommand |
||
| 23 | { |
||
| 24 | protected $skeletonDirs; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * {@inheritdoc} |
||
| 28 | */ |
||
| 29 | public function configure() |
||
| 30 | { |
||
| 31 | parent::configure(); |
||
| 32 | |||
| 33 | $this |
||
| 34 | ->setName('victoire:generate:widget') |
||
| 35 | ->setDefinition([ |
||
| 36 | new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the widget bundle to create'), |
||
| 37 | new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'), |
||
| 38 | new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'), |
||
| 39 | new InputOption('orgname', '', InputOption::VALUE_REQUIRED, 'Your organisation name'), |
||
| 40 | new InputOption('widget-name', '', InputOption::VALUE_REQUIRED, 'The widget name'), |
||
| 41 | new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)'), |
||
| 42 | new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'), |
||
| 43 | new InputOption('fields', '', InputOption::VALUE_REQUIRED, 'The fields to create with the new entity'), |
||
| 44 | new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), |
||
| 45 | new InputOption('parent', '', InputOption::VALUE_REQUIRED, 'The widget this widget will extends'), |
||
| 46 | new InputOption('packagist-parent-name', '', InputOption::VALUE_REQUIRED, 'The packagist name of the widget you want to extends'), |
||
| 47 | new InputOption('content-resolver', '', InputOption::VALUE_NONE, 'Whether to generate a blank ContentResolver to customize widget rendering logic'), |
||
| 48 | new InputOption('cache', '', InputOption::VALUE_NONE, 'Use redis cache to store widgets until next modification'), |
||
| 49 | ]) |
||
| 50 | ->setDescription('Generate a new widget') |
||
| 51 | ->setHelp(<<<'EOT' |
||
| 52 | The <info>victoire:generate:widget</info> command helps you to generate new widgets. |
||
| 53 | |||
| 54 | By default, the command interacts with the developer to tweak the generation. |
||
| 55 | Any passed option will be used as a default value for the interaction |
||
| 56 | (<comment>--widget-name</comment> is the only one needed if you follow the |
||
| 57 | conventions): |
||
| 58 | |||
| 59 | <info>php app/console victoire:generate:widget --widget-name=myAwesomeWidget</info> |
||
| 60 | |||
| 61 | If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options: |
||
| 62 | |||
| 63 | Love you guys, you're awesome xxx |
||
| 64 | EOT |
||
| 65 | ); |
||
| 66 | } |
||
| 67 | |||
| 68 | /** |
||
| 69 | * Take arguments and options defined in $this->interact() and generate a new Widget. |
||
| 70 | * |
||
| 71 | * @param InputInterface $input |
||
| 72 | * @param OutputInterface $output |
||
| 73 | * |
||
| 74 | * @see Command |
||
| 75 | * |
||
| 76 | * @throws \InvalidArgumentException When namespace doesn't end with Bundle |
||
| 77 | * @throws \RuntimeException When bundle can't be executed |
||
| 78 | * |
||
| 79 | * @return int|null |
||
| 80 | */ |
||
| 81 | protected function execute(InputInterface $input, OutputInterface $output) |
||
| 82 | { |
||
| 83 | $questionHelper = $this->getQuestionHelper(); |
||
| 84 | |||
| 85 | if ($input->isInteractive()) { |
||
| 86 | $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); |
||
| 87 | if (!$questionHelper->ask($input, $output, $question)) { |
||
| 88 | $output->writeln('<error>Command aborted</error>'); |
||
| 89 | |||
| 90 | return 1; |
||
| 91 | } |
||
| 92 | } |
||
| 93 | |||
| 94 | foreach (['namespace', 'dir'] as $option) { |
||
| 95 | if (null === $input->getOption($option)) { |
||
| 96 | throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); |
||
| 97 | } |
||
| 98 | } |
||
| 99 | |||
| 100 | $namespace = Validators::validateBundleNamespace($input->getOption('namespace')); |
||
| 101 | |||
| 102 | if (!$bundle = $input->getOption('bundle-name')) { |
||
| 103 | $bundle = strtr($namespace, ['\\' => '']); |
||
| 104 | } |
||
| 105 | |||
| 106 | $orgname = $input->getOption('orgname'); |
||
| 107 | |||
| 108 | if (null === $input->getOption('orgname')) { |
||
| 109 | $orgname = $input->setOption('orgname', 'friendsofvictoire'); |
||
| 110 | } |
||
| 111 | |||
| 112 | $parent = $input->getOption('parent'); |
||
| 113 | |||
| 114 | if (null === $input->getOption('parent')) { |
||
| 115 | $parent = $input->setOption('parent', null); |
||
| 116 | } |
||
| 117 | |||
| 118 | $packagistParentName = $input->getOption('packagist-parent-name'); |
||
| 119 | |||
| 120 | if (null === $input->getOption('packagist-parent-name')) { |
||
| 121 | $packagistParentName = $input->setOption('packagist-parent-name', null); |
||
| 122 | } |
||
| 123 | |||
| 124 | $bundle = Validators::validateBundleName($bundle); |
||
| 125 | $dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); |
||
| 126 | |||
| 127 | if (null === $input->getOption('format')) { |
||
| 128 | $input->setOption('format', 'annotation'); |
||
| 129 | } |
||
| 130 | |||
| 131 | $format = Validators::validateFormat($input->getOption('format')); |
||
| 132 | $structure = $input->getOption('structure'); |
||
| 133 | |||
| 134 | $contentResolver = $input->getOption('content-resolver'); |
||
| 135 | $cache = $input->getOption('cache'); |
||
| 136 | |||
| 137 | $questionHelper->writeSection($output, 'Bundle generation'); |
||
| 138 | |||
| 139 | if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { |
||
| 140 | $dir = getcwd().'/'.$dir; |
||
| 141 | } |
||
| 142 | |||
| 143 | $fields = $this->parseFields($input->getOption('fields')); |
||
| 144 | |||
| 145 | $parentContentResolver = $this->getContainer()->has('victoire_core.widget_'.strtolower($parent).'_content_resolver'); |
||
| 146 | |||
| 147 | $generator = $this->getGenerator(); |
||
| 148 | $generator->generate($namespace, $bundle, $dir, $format, $structure, $fields, $parent, $packagistParentName, $contentResolver, $parentContentResolver, $orgname, $cache); |
||
| 149 | |||
| 150 | $output->writeln('Generating the bundle code: <info>OK</info>'); |
||
| 151 | |||
| 152 | $errors = []; |
||
| 153 | $runner = $questionHelper->getRunner($output, $errors); |
||
| 154 | |||
| 155 | // check that the namespace is already autoloaded |
||
| 156 | $runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); |
||
| 157 | |||
| 158 | // register the bundle in the Kernel class |
||
| 159 | $runner($this->updateKernel($questionHelper, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); |
||
| 160 | |||
| 161 | $questionHelper->writeGeneratorSummary($output, $errors); |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * get a generator for given widget and type, and attach it skeleton dirs. |
||
| 166 | * |
||
| 167 | * @return $generator |
||
| 168 | */ |
||
| 169 | View Code Duplication | protected function getEntityGenerator() |
|
| 170 | { |
||
| 171 | $dirs[] = $this->getContainer()->get('file_locator')->locate('@VictoireWidgetBundle/Resources/skeleton/'); |
||
| 172 | $dirs[] = $this->getContainer()->get('file_locator')->locate('@VictoireWidgetBundle/Resources/'); |
||
| 173 | |||
| 174 | $generator = $this->createEntityGenerator(); |
||
| 175 | |||
| 176 | $this->skeletonDirs = array_merge($this->getSkeletonDirs(), $dirs); |
||
| 177 | $generator->setSkeletonDirs($this->skeletonDirs); |
||
| 178 | $this->setGenerator($generator); |
||
| 179 | |||
| 180 | return $generator; |
||
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * get a generator for given widget and type, and attach it skeleton dirs. |
||
| 185 | * |
||
| 186 | * @return $generator |
||
| 187 | */ |
||
| 188 | View Code Duplication | protected function getGenerator(BundleInterface $bundle = null) |
|
| 189 | { |
||
| 190 | $dirs[] = $this->getContainer()->get('file_locator')->locate('@VictoireWidgetBundle/Resources/skeleton/'); |
||
| 191 | $dirs[] = $this->getContainer()->get('file_locator')->locate('@VictoireWidgetBundle/Resources/'); |
||
| 192 | |||
| 193 | $generator = $this->createWidgetGenerator(); |
||
| 194 | |||
| 195 | $this->skeletonDirs = array_merge($this->getSkeletonDirs(), $dirs); |
||
| 196 | $generator->setSkeletonDirs($this->skeletonDirs); |
||
| 197 | $this->setGenerator($generator); |
||
| 198 | |||
| 199 | return $generator; |
||
| 200 | } |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Collect options and arguments. |
||
| 204 | * |
||
| 205 | * @param InputInterface $input |
||
| 206 | * @param OutputInterface $output |
||
| 207 | * |
||
| 208 | * @return void |
||
| 209 | */ |
||
| 210 | protected function interact(InputInterface $input, OutputInterface $output) |
||
| 211 | { |
||
| 212 | $questionHelper = $this->getQuestionHelper(); |
||
| 213 | $questionHelper->writeSection($output, 'Welcome to the Victoire widget bundle generator'); |
||
| 214 | |||
| 215 | /////////////////////// |
||
| 216 | // // |
||
| 217 | // Create Bundle // |
||
| 218 | // // |
||
| 219 | /////////////////////// |
||
| 220 | |||
| 221 | // namespace |
||
| 222 | $namespace = null; |
||
| 223 | try { |
||
| 224 | $namespace = $input->getOption('namespace') ? Validators::validateBundleNamespace($input->getOption('namespace')) : null; |
||
| 225 | } catch (\Exception $error) { |
||
| 226 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); |
||
| 227 | } |
||
| 228 | |||
| 229 | if (null === $namespace) { |
||
| 230 | $output->writeln([ |
||
| 231 | '', |
||
| 232 | 'Your application code must be written in <comment>widget bundles</comment>. This command helps', |
||
| 233 | 'you generate them easily.', |
||
| 234 | '', |
||
| 235 | 'Each widget is hosted under a namespace (like <comment>Victoire/Widget/YourAwesomeWidgetNameBundle</comment>).', |
||
| 236 | '', |
||
| 237 | 'If you want for example a BlogWidget, the Widget Name should be Blog', |
||
| 238 | ]); |
||
| 239 | |||
| 240 | $question = new Question($questionHelper->getQuestion('Widget name', $input->getOption('bundle-name'))); |
||
| 241 | $question->setValidator(function ($answer) { |
||
| 242 | return self::validateWidgetName($answer, false); |
||
| 243 | }); |
||
| 244 | |||
| 245 | $name = $questionHelper->ask( |
||
| 246 | $input, |
||
| 247 | $output, |
||
| 248 | $question |
||
| 249 | ); |
||
| 250 | |||
| 251 | $bundle = 'VictoireWidget'.$name.'Bundle'; |
||
| 252 | $input->setOption('bundle-name', $bundle); |
||
| 253 | $namespace = 'Victoire\\Widget\\'.$name.'Bundle'; |
||
| 254 | $input->setOption('namespace', $namespace); |
||
| 255 | } |
||
| 256 | |||
| 257 | $orgname = $input->getOption('orgname'); |
||
| 258 | |||
| 259 | if (null === $orgname) { |
||
| 260 | $output->writeln([ |
||
| 261 | '', |
||
| 262 | 'A composer.json file will be generated, we need to know under which organisation you will publish the widget', |
||
| 263 | '', |
||
| 264 | 'The default organisation will be friendsofvictoire', |
||
| 265 | ]); |
||
| 266 | $question = new Question($questionHelper->getQuestion('Under which organisation do you want to publish your widget ?', 'friendsofvictoire'), 'friendsofvictoire'); |
||
| 267 | |||
| 268 | $orgname = $questionHelper->ask($input, $output, $question); |
||
| 269 | } |
||
| 270 | |||
| 271 | $input->setOption('orgname', $orgname); |
||
| 272 | |||
| 273 | $parent = $input->getOption('parent'); |
||
| 274 | |||
| 275 | $question = new ConfirmationQuestion($questionHelper->getQuestion('Does your widget extends another widget ?', 'no', '?'), false); |
||
| 276 | |||
| 277 | if (null === $parent && $questionHelper->ask($input, $output, $question)) { |
||
| 278 | $output->writeln([ |
||
| 279 | '', |
||
| 280 | 'A widget can extends another to reproduce it\'s behavior', |
||
| 281 | '', |
||
| 282 | 'If you wabt to do so, please give the name of the widget to extend', |
||
| 283 | '', |
||
| 284 | 'If you want to extends the TestWidget, the widget name should be Test', |
||
| 285 | ]); |
||
| 286 | |||
| 287 | $question = new Question($questionHelper->getQuestion('Parent widget name', false)); |
||
| 288 | $question->setValidator(function ($answer) { |
||
| 289 | return self::validateWidgetName($answer, false); |
||
| 290 | }); |
||
| 291 | $parent = $questionHelper->ask($input, $output, $question); |
||
| 292 | |||
| 293 | $input->setOption('parent', $parent); |
||
| 294 | |||
| 295 | $packagistParentName = 'friendsofvictoire/'.strtolower($parent).'-widget'; |
||
| 296 | $question = new Question($questionHelper->getQuestion('Parent widget packagist name', $packagistParentName)); |
||
| 297 | |||
| 298 | $parent = $questionHelper->ask($input, $output, $question); |
||
| 299 | |||
| 300 | $input->setOption('packagist-parent-name', $packagistParentName); |
||
| 301 | } |
||
| 302 | |||
| 303 | $dir = dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; |
||
| 304 | |||
| 305 | $output->writeln([ |
||
| 306 | '', |
||
| 307 | 'The bundle can be generated anywhere. The suggested default directory uses', |
||
| 308 | 'the standard conventions.', |
||
| 309 | '', |
||
| 310 | ]); |
||
| 311 | |||
| 312 | $question = new Question($questionHelper->getQuestion('Target directory', $dir), $dir); |
||
| 313 | $question->setValidator(function ($dir) use ($bundle, $namespace) { |
||
| 314 | return Validators::validateTargetDir($dir, $bundle, $namespace); |
||
| 315 | }); |
||
| 316 | $dir = $questionHelper->ask($input, $output, $question); |
||
| 317 | $input->setOption('dir', $dir); |
||
| 318 | |||
| 319 | // format |
||
| 320 | $format = null; |
||
| 321 | try { |
||
| 322 | $format = $input->getOption('format') ? Validators::validateFormat($input->getOption('format')) : null; |
||
| 323 | } catch (\Exception $error) { |
||
| 324 | $output->writeln($questionHelper->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); |
||
| 325 | } |
||
| 326 | |||
| 327 | if (null === $format) { |
||
| 328 | $output->writeln([ |
||
| 329 | '', |
||
| 330 | 'Determine the format to use for the generated configuration.', |
||
| 331 | '', |
||
| 332 | ]); |
||
| 333 | |||
| 334 | $question = new Question($questionHelper->getQuestion('Configuration format (yml, xml, php, or annotation)', 'annotation'), 'annotation'); |
||
| 335 | $question->setValidator( |
||
| 336 | ['Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'] |
||
| 337 | ); |
||
| 338 | $format = $questionHelper->ask($input, $output, $question); |
||
| 339 | $input->setOption('format', $format); |
||
| 340 | } |
||
| 341 | |||
| 342 | $input->setOption('structure', false); |
||
| 343 | |||
| 344 | $contentResolver = $input->getOption('content-resolver'); |
||
| 345 | |||
| 346 | $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want to customize widget rendering logic ?', 'no', '?'), false); |
||
| 347 | if (!$contentResolver && $questionHelper->ask($input, $output, $question)) { |
||
| 348 | $contentResolver = true; |
||
| 349 | } |
||
| 350 | $input->setOption('content-resolver', $contentResolver); |
||
| 351 | |||
| 352 | /////////////////////// |
||
| 353 | // // |
||
| 354 | // Create Entity // |
||
| 355 | // // |
||
| 356 | /////////////////////// |
||
| 357 | |||
| 358 | $input->setOption('fields', $this->addFields($input, $output, $questionHelper)); |
||
| 359 | $entity = 'Widget'.$name; |
||
| 360 | $input->setOption('entity', $bundle.':'.$entity); |
||
| 361 | |||
| 362 | $cache = $input->getOption('cache'); |
||
| 363 | $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want use cache for this widget ?', 'no', '?'), false); |
||
| 364 | if (null !== $cache) { |
||
| 365 | $cache = $questionHelper->ask($input, $output, $question); |
||
| 366 | } |
||
| 367 | $input->setOption('cache', $cache); |
||
| 368 | |||
| 369 | // summary |
||
| 370 | $output->writeln([ |
||
| 371 | '', |
||
| 372 | $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), |
||
|
0 ignored issues
–
show
|
|||
| 373 | '', |
||
| 374 | sprintf("You are going to generate a \"<info>%s\\%s</info>\" widget bundle\nin \"<info>%s</info>\" using the \"<info>%s</info>\" format.", $namespace, $bundle, $dir, $format), |
||
| 375 | '', |
||
| 376 | ]); |
||
| 377 | } |
||
| 378 | |||
| 379 | /** |
||
| 380 | * Check that provided widget name is correct. |
||
| 381 | * |
||
| 382 | * @param string $widget |
||
| 383 | * |
||
| 384 | * @return string $widget |
||
| 385 | */ |
||
| 386 | public static function validateWidgetName($widget) |
||
| 387 | { |
||
| 388 | if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $widget)) { |
||
| 389 | throw new \InvalidArgumentException('The widget name contains invalid characters.'); |
||
| 390 | } |
||
| 391 | |||
| 392 | if (!preg_match('/^([A-Z][a-z]+)+$/', $widget)) { |
||
| 393 | throw new \InvalidArgumentException('The widget name must be PascalCased.'); |
||
| 394 | } |
||
| 395 | |||
| 396 | return $widget; |
||
| 397 | } |
||
| 398 | |||
| 399 | /** |
||
| 400 | * Instanciate a new WidgetGenerator. |
||
| 401 | * |
||
| 402 | * @return $generator |
||
| 403 | */ |
||
| 404 | protected function createWidgetGenerator() |
||
| 405 | { |
||
| 406 | $generator = new WidgetGenerator($this->getContainer()->get('filesystem')); |
||
| 407 | $generator->setTemplating($this->getContainer()->get('twig')); |
||
| 408 | |||
| 409 | return $generator; |
||
| 410 | } |
||
| 411 | |||
| 412 | /** |
||
| 413 | * Instanciate a new Entity generator. |
||
| 414 | * |
||
| 415 | * @return $generator |
||
| 416 | */ |
||
| 417 | protected function createEntityGenerator() |
||
| 418 | { |
||
| 419 | return new DoctrineEntityGenerator($this->getContainer()->get('filesystem'), $this->getContainer()->get('doctrine')); |
||
| 420 | } |
||
| 421 | |||
| 422 | /** |
||
| 423 | * transform console's output string fields into an array of fields. |
||
| 424 | * |
||
| 425 | * @param string $input |
||
| 426 | * |
||
| 427 | * @return array $fields |
||
| 428 | */ |
||
| 429 | private function parseFields($input) |
||
| 430 | { |
||
| 431 | if (is_array($input)) { |
||
| 432 | return $input; |
||
| 433 | } |
||
| 434 | |||
| 435 | $fields = []; |
||
| 436 | foreach (explode(' ', $input) as $value) { |
||
| 437 | $elements = explode(':', $value); |
||
| 438 | $name = $elements[0]; |
||
| 439 | if (strlen($name)) { |
||
| 440 | $type = isset($elements[1]) ? $elements[1] : 'string'; |
||
| 441 | preg_match_all('/(.*)\((.*)\)/', $type, $matches); |
||
| 442 | $type = isset($matches[1][0]) ? $matches[1][0] : $type; |
||
| 443 | $length = isset($matches[2][0]) ? $matches[2][0] : null; |
||
| 444 | |||
| 445 | $fields[$name] = ['fieldName' => $name, 'type' => $type, 'length' => $length]; |
||
| 446 | } |
||
| 447 | } |
||
| 448 | |||
| 449 | return $fields; |
||
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Interactively ask user to add field to his new Entity. |
||
| 454 | * |
||
| 455 | * @param InputInterface $input |
||
| 456 | * @param OutputInterface $output |
||
| 457 | * @param QuestionHelper $questionHelper |
||
| 458 | * |
||
| 459 | * @return $fields |
||
| 460 | */ |
||
| 461 | private function addFields(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper) |
||
| 462 | { |
||
| 463 | $fields = $this->parseFields($input->getOption('fields')); |
||
| 464 | $output->writeln([ |
||
| 465 | '', |
||
| 466 | 'Instead of starting with a blank entity, you can add some fields now.', |
||
| 467 | 'Note that the primary key will be added automatically (named <comment>id</comment>).', |
||
| 468 | '', |
||
| 469 | ]); |
||
| 470 | $output->write('<info>Available types:</info> '); |
||
| 471 | |||
| 472 | $types = array_keys(Type::getTypesMap()); |
||
| 473 | $count = 20; |
||
| 474 | foreach ($types as $i => $type) { |
||
| 475 | if ($count > 50) { |
||
| 476 | $count = 0; |
||
| 477 | $output->writeln(''); |
||
| 478 | } |
||
| 479 | $count += strlen($type); |
||
| 480 | $output->write(sprintf('<comment>%s</comment>', $type)); |
||
| 481 | if (count($types) != $i + 1) { |
||
| 482 | $output->write(', '); |
||
| 483 | } else { |
||
| 484 | $output->write('.'); |
||
| 485 | } |
||
| 486 | } |
||
| 487 | $output->writeln(''); |
||
| 488 | |||
| 489 | $fieldValidator = function ($type) use ($types) { |
||
| 490 | if (!in_array($type, $types)) { |
||
| 491 | throw new \InvalidArgumentException(sprintf('Invalid type "%s".', $type)); |
||
| 492 | } |
||
| 493 | |||
| 494 | return $type; |
||
| 495 | }; |
||
| 496 | |||
| 497 | $lengthValidator = function ($length) { |
||
| 498 | if (!$length) { |
||
| 499 | return $length; |
||
| 500 | } |
||
| 501 | |||
| 502 | $result = filter_var($length, FILTER_VALIDATE_INT, [ |
||
| 503 | 'options' => ['min_range' => 1], |
||
| 504 | ]); |
||
| 505 | |||
| 506 | if (false === $result) { |
||
| 507 | throw new \InvalidArgumentException(sprintf('Invalid length "%s".', $length)); |
||
| 508 | } |
||
| 509 | |||
| 510 | return $length; |
||
| 511 | }; |
||
| 512 | |||
| 513 | while (true) { |
||
| 514 | $output->writeln(''); |
||
| 515 | $generator = $this->getEntityGenerator(); |
||
| 516 | |||
| 517 | $question = new Question($questionHelper->getQuestion('New field name (press <return> to stop adding fields)', null)); |
||
| 518 | $question->setValidator( |
||
| 519 | function ($name) use ($fields, $generator) { |
||
| 520 | if (isset($fields[$name]) || 'id' == $name) { |
||
| 521 | throw new \InvalidArgumentException(sprintf('Field "%s" is already defined.', $name)); |
||
| 522 | } |
||
| 523 | |||
| 524 | // check reserved words by database |
||
| 525 | if ($generator->isReservedKeyword($name)) { |
||
| 526 | throw new \InvalidArgumentException(sprintf('Name "%s" is a reserved word.', $name)); |
||
| 527 | } |
||
| 528 | // check reserved words by victoire |
||
| 529 | if ($this->isReservedKeyword($name)) { |
||
| 530 | throw new \InvalidArgumentException(sprintf('Name "%s" is a Victoire reserved word.', $name)); |
||
| 531 | } |
||
| 532 | |||
| 533 | return $name; |
||
| 534 | } |
||
| 535 | ); |
||
| 536 | |||
| 537 | $columnName = $questionHelper->ask($input, $output, $question); |
||
| 538 | if (!$columnName) { |
||
| 539 | break; |
||
| 540 | } |
||
| 541 | |||
| 542 | $defaultType = 'string'; |
||
| 543 | |||
| 544 | // try to guess the type by the column name prefix/suffix |
||
| 545 | if (substr($columnName, -3) == '_at') { |
||
| 546 | $defaultType = 'datetime'; |
||
| 547 | } elseif (substr($columnName, -3) == '_id') { |
||
| 548 | $defaultType = 'integer'; |
||
| 549 | } elseif (substr($columnName, 0, 3) == 'is_') { |
||
| 550 | $defaultType = 'boolean'; |
||
| 551 | } elseif (substr($columnName, 0, 4) == 'has_') { |
||
| 552 | $defaultType = 'boolean'; |
||
| 553 | } |
||
| 554 | |||
| 555 | $question = new Question($questionHelper->getQuestion('Field type', $defaultType), $defaultType); |
||
| 556 | $question->setValidator($fieldValidator); |
||
| 557 | $question->setAutocompleterValues($types); |
||
| 558 | $type = $questionHelper->ask($input, $output, $question); |
||
| 559 | |||
| 560 | $data = ['columnName' => $columnName, 'fieldName' => lcfirst(Container::camelize($columnName)), 'type' => $type]; |
||
| 561 | |||
| 562 | if ($type == 'string') { |
||
| 563 | $question = new Question($questionHelper->getQuestion('Field length', 255), 255); |
||
| 564 | $question->setValidator($lengthValidator); |
||
| 565 | $data['length'] = $questionHelper->ask($input, $output, $question); |
||
| 566 | } |
||
| 567 | |||
| 568 | $fields[$columnName] = $data; |
||
| 569 | } |
||
| 570 | |||
| 571 | return $fields; |
||
| 572 | } |
||
| 573 | |||
| 574 | /** |
||
| 575 | * Validate Entity short namepace. |
||
| 576 | * |
||
| 577 | * @param string $shortcut |
||
| 578 | * |
||
| 579 | * @return $shortcut |
||
| 580 | */ |
||
| 581 | protected function parseShortcutNotation($shortcut) |
||
| 582 | { |
||
| 583 | $entity = str_replace('/', '\\', $shortcut); |
||
| 584 | |||
| 585 | if (false === $pos = strpos($entity, ':')) { |
||
| 586 | throw new \InvalidArgumentException(sprintf('The entity name must contain a : ("%s" given, expecting something like AcmeBlogBundle:Blog/Post)', $entity)); |
||
| 587 | } |
||
| 588 | |||
| 589 | return [substr($entity, 0, $pos), substr($entity, $pos + 1)]; |
||
| 590 | } |
||
| 591 | |||
| 592 | protected function isReservedKeyword($keyword) |
||
| 593 | { |
||
| 594 | return in_array($keyword, ['widget']); |
||
| 595 | } |
||
| 596 | } |
||
| 597 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: