vendor/pimcore/pimcore/bundles/GeneratorBundle/Command/BaseGenerateBundleCommand.php line 22

Open in your IDE?
  1. <?php
  2. namespace Pimcore\Bundle\GeneratorBundle\Command;
  3. use Pimcore\Bundle\GeneratorBundle\Generator\BundleGenerator;
  4. use Pimcore\Bundle\GeneratorBundle\Manipulator\ConfigurationManipulator;
  5. use Pimcore\Bundle\GeneratorBundle\Manipulator\KernelManipulator;
  6. use Pimcore\Bundle\GeneratorBundle\Manipulator\RoutingManipulator;
  7. use Pimcore\Bundle\GeneratorBundle\Model\Bundle;
  8. use Symfony\Component\Console\Input\InputInterface;
  9. use Symfony\Component\Console\Input\InputOption;
  10. use Symfony\Component\Console\Output\OutputInterface;
  11. use Symfony\Component\Console\Question\ConfirmationQuestion;
  12. use Symfony\Component\Console\Question\Question;
  13. use Symfony\Component\HttpKernel\KernelInterface;
  14. /**
  15.  * Generates bundles.
  16.  *
  17.  * The following class is copied from \Sensio\Bundle\GeneratorBundle\Command\GenerateBundleCommand
  18.  */
  19. class BaseGenerateBundleCommand extends BaseGeneratorCommand
  20. {
  21.     /**
  22.      * @see Command
  23.      */
  24.     protected function configure()
  25.     {
  26.         $this
  27.             ->setName('generate:bundle')
  28.             ->setDescription('Generates a bundle')
  29.             ->setDefinition([
  30.                 new InputOption('namespace'''InputOption::VALUE_REQUIRED'The namespace of the bundle to create'),
  31.                 new InputOption('dir'''InputOption::VALUE_REQUIRED'The directory where to create the bundle''src/'),
  32.                 new InputOption('bundle-name'''InputOption::VALUE_REQUIRED'The optional bundle name'),
  33.                 new InputOption('format'''InputOption::VALUE_REQUIRED'Use the format for configuration files (php, xml, yml, or annotation)'),
  34.                 new InputOption('shared'''InputOption::VALUE_NONE'Are you planning on sharing this bundle across multiple applications?'),
  35.             ])
  36.             ->setHelp(<<<EOT
  37. The <info>%command.name%</info> command helps you generates new bundles.
  38. By default, the command interacts with the developer to tweak the generation.
  39. Any passed option will be used as a default value for the interaction
  40. (<comment>--namespace</comment> is the only one needed if you follow the
  41. conventions):
  42. <info>php %command.full_name% --namespace=Acme/BlogBundle</info>
  43. Note that you can use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any
  44. problems.
  45. If you want to disable any user interaction, use <comment>--no-interaction</comment> but don't forget to pass all needed options:
  46. <info>php %command.full_name% --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info>
  47. Note that the bundle namespace must end with "Bundle".
  48. EOT
  49.             )
  50.         ;
  51.     }
  52.     /**
  53.      * @see Command
  54.      *
  55.      * @throws \InvalidArgumentException When namespace doesn't end with Bundle
  56.      * @throws \RuntimeException         When bundle can't be executed
  57.      */
  58.     protected function execute(InputInterface $inputOutputInterface $output)
  59.     {
  60.         $questionHelper $this->getQuestionHelper();
  61.         $bundle $this->createBundleObject($input);
  62.         $questionHelper->writeSection($output'Bundle generation');
  63.         /** @var BundleGenerator $generator */
  64.         $generator $this->getGenerator();
  65.         $output->writeln(sprintf(
  66.             '> Generating a sample bundle skeleton into <info>%s</info>',
  67.             $this->makePathRelative($bundle->getTargetDirectory())
  68.         ));
  69.         $generator->generateBundle($bundle);
  70.         $errors = [];
  71.         $runner $questionHelper->getRunner($output$errors);
  72.         // check that the namespace is already autoloaded
  73.         $runner($this->checkAutoloader($output$bundle));
  74.         // register the bundle in the Kernel class
  75.         $runner($this->updateKernel($output$this->getContainer()->get('kernel'), $bundle));
  76.         // routing importing
  77.         $runner($this->updateRouting($output$bundle));
  78.         if (!$bundle->shouldGenerateDependencyInjectionDirectory()) {
  79.             // we need to import their services.yml manually!
  80.             $runner($this->updateConfiguration($output$bundle));
  81.         }
  82.         $questionHelper->writeGeneratorSummary($output$errors);
  83.     }
  84.     protected function interact(InputInterface $inputOutputInterface $output)
  85.     {
  86.         $questionHelper $this->getQuestionHelper();
  87.         $questionHelper->writeSection($output'Welcome to the Symfony bundle generator!');
  88.         /*
  89.          * shared option
  90.          */
  91.         $shared $input->getOption('shared');
  92.         // ask, but use $shared as the default
  93.         $question = new ConfirmationQuestion($questionHelper->getQuestion(
  94.             'Are you planning on sharing this bundle across multiple applications?',
  95.             $shared 'yes' 'no'
  96.         ), $shared);
  97.         $shared $questionHelper->ask($input$output$question);
  98.         $input->setOption('shared'$shared);
  99.         /*
  100.          * namespace option
  101.          */
  102.         $namespace $input->getOption('namespace');
  103.         $output->writeln([
  104.             '',
  105.             'Your application code must be written in <comment>bundles</comment>. This command helps',
  106.             'you generate them easily.',
  107.             '',
  108.         ]);
  109.         $askForBundleName true;
  110.         if ($shared) {
  111.             // a shared bundle, so it should probably have a vendor namespace
  112.             $output->writeln([
  113.                 'Each bundle is hosted under a namespace (like <comment>Acme/BlogBundle</comment>).',
  114.                 'The namespace should begin with a "vendor" name like your company name, your',
  115.                 'project name, or your client name, followed by one or more optional category',
  116.                 'sub-namespaces, and it should end with the bundle name itself',
  117.                 '(which must have <comment>Bundle</comment> as a suffix).',
  118.                 '',
  119.                 'See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#bundle-name for more',
  120.                 'details on bundle naming conventions.',
  121.                 '',
  122.                 'Use <comment>/</comment> instead of <comment>\\ </comment>for the namespace delimiter to avoid any problems.',
  123.                 '',
  124.             ]);
  125.             $question = new Question($questionHelper->getQuestion(
  126.                 'Bundle namespace',
  127.                 $namespace
  128.             ), $namespace);
  129.             $question->setValidator(function ($answer) {
  130.                 return Validators::validateBundleNamespace($answertrue);
  131.             });
  132.             $namespace $questionHelper->ask($input$output$question);
  133.         } else {
  134.             // a simple application bundle
  135.             $output->writeln([
  136.                 'Give your bundle a descriptive name, like <comment>BlogBundle</comment>.',
  137.             ]);
  138.             $question = new Question($questionHelper->getQuestion(
  139.                 'Bundle name',
  140.                 $namespace
  141.             ), $namespace);
  142.             $question->setValidator(function ($inputNamespace) {
  143.                 return Validators::validateBundleNamespace($inputNamespacefalse);
  144.             });
  145.             $namespace $questionHelper->ask($input$output$question);
  146.             if (strpos($namespace'\\') === false) {
  147.                 // this is a bundle name (FooBundle) not a namespace (Acme\FooBundle)
  148.                 // so this is the bundle name (and it is also the namespace)
  149.                 $input->setOption('bundle-name'$namespace);
  150.                 $askForBundleName false;
  151.             }
  152.         }
  153.         $input->setOption('namespace'$namespace);
  154.         /*
  155.          * bundle-name option
  156.          */
  157.         if ($askForBundleName) {
  158.             $bundle $input->getOption('bundle-name');
  159.             // no bundle yet? Get a default from the namespace
  160.             if (!$bundle) {
  161.                 $bundle strtr($namespace, ['\\Bundle\\' => '''\\' => '']);
  162.             }
  163.             $output->writeln([
  164.                 '',
  165.                 'In your code, a bundle is often referenced by its name. It can be the',
  166.                 'concatenation of all namespace parts but it\'s really up to you to come',
  167.                 'up with a unique name (a good practice is to start with the vendor name).',
  168.                 'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.',
  169.                 '',
  170.             ]);
  171.             $question = new Question($questionHelper->getQuestion(
  172.                 'Bundle name',
  173.                 $bundle
  174.             ), $bundle);
  175.             $question->setValidator(
  176.                 ['Pimcore\Bundle\GeneratorBundle\Command\Validators''validateBundleName']
  177.             );
  178.             $bundle $questionHelper->ask($input$output$question);
  179.             $input->setOption('bundle-name'$bundle);
  180.         }
  181.         /*
  182.          * dir option
  183.          */
  184.         // defaults to src/ in the option
  185.         $dir $input->getOption('dir');
  186.         $output->writeln([
  187.             '',
  188.             'Bundles are usually generated into the <info>src/</info> directory. Unless you\'re',
  189.             'doing something custom, hit enter to keep this default!',
  190.             '',
  191.         ]);
  192.         $question = new Question($questionHelper->getQuestion(
  193.             'Target Directory',
  194.             $dir
  195.         ), $dir);
  196.         $dir $questionHelper->ask($input$output$question);
  197.         $input->setOption('dir'$dir);
  198.         /*
  199.          * format option
  200.          */
  201.         $format $input->getOption('format');
  202.         if (!$format) {
  203.             $format $shared 'xml' 'annotation';
  204.         }
  205.         $output->writeln([
  206.             '',
  207.             'What format do you want to use for your generated configuration?',
  208.             '',
  209.         ]);
  210.         $question = new Question($questionHelper->getQuestion(
  211.             'Configuration format (annotation, yml, xml, php)',
  212.             $format
  213.         ), $format);
  214.         $question->setValidator(function ($format) {
  215.             return Validators::validateFormat($format);
  216.         });
  217.         $question->setAutocompleterValues(['annotation''yml''xml''php']);
  218.         $format $questionHelper->ask($input$output$question);
  219.         $input->setOption('format'$format);
  220.     }
  221.     protected function checkAutoloader(OutputInterface $outputBundle $bundle)
  222.     {
  223.         $output->writeln('> Checking that the bundle is autoloaded');
  224.         if (!class_exists($bundle->getBundleClassName())) {
  225.             return [
  226.                 '- Edit the <comment>composer.json</comment> file and register the bundle',
  227.                 '  namespace in the "autoload" section:',
  228.                 '',
  229.             ];
  230.         }
  231.     }
  232.     protected function updateKernel(OutputInterface $outputKernelInterface $kernelBundle $bundle)
  233.     {
  234.         $kernelManipulator = new KernelManipulator($kernel);
  235.         $output->writeln(sprintf(
  236.             '> Enabling the bundle inside <info>%s</info>',
  237.             $this->makePathRelative($kernelManipulator->getFilename())
  238.         ));
  239.         try {
  240.             $ret $kernelManipulator->addBundle($bundle->getBundleClassName());
  241.             if (!$ret) {
  242.                 $reflected = new \ReflectionObject($kernel);
  243.                 return [
  244.                     sprintf('- Edit <comment>%s</comment>'$reflected->getFilename()),
  245.                     '  and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:',
  246.                     '',
  247.                     sprintf('    <comment>new %s(),</comment>'$bundle->getBundleClassName()),
  248.                     '',
  249.                 ];
  250.             }
  251.         } catch (\RuntimeException $e) {
  252.             return [
  253.                 sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.'$bundle->getBundleClassName()),
  254.                 '',
  255.             ];
  256.         }
  257.     }
  258.     protected function updateRouting(OutputInterface $outputBundle $bundle)
  259.     {
  260.         $targetRoutingPath $this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml';
  261.         $output->writeln(sprintf(
  262.             '> Importing the bundle\'s routes from the <info>%s</info> file',
  263.             $this->makePathRelative($targetRoutingPath)
  264.         ));
  265.         $routing = new RoutingManipulator($targetRoutingPath);
  266.         try {
  267.             $ret $routing->addResource($bundle->getName(), $bundle->getConfigurationFormat());
  268.             if (!$ret) {
  269.                 if ('annotation' === $bundle->getConfigurationFormat()) {
  270.                     $help sprintf("        <comment>resource: \"@%s/Controller/\"</comment>\n        <comment>type:     annotation</comment>\n"$bundle->getName());
  271.                 } else {
  272.                     $help sprintf("        <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n"$bundle->getName(), $bundle->getConfigurationFormat());
  273.                 }
  274.                 $help .= "        <comment>prefix:   /</comment>\n";
  275.                 return [
  276.                     '- Import the bundle\'s routing resource in the app\'s main routing file:',
  277.                     '',
  278.                     sprintf('    <comment>%s:</comment>'$bundle->getName()),
  279.                     $help,
  280.                     '',
  281.                 ];
  282.             }
  283.         } catch (\RuntimeException $e) {
  284.             return [
  285.                 sprintf('Bundle <comment>%s</comment> is already imported.'$bundle->getName()),
  286.                 '',
  287.             ];
  288.         }
  289.     }
  290.     protected function updateConfiguration(OutputInterface $outputBundle $bundle)
  291.     {
  292.         $targetConfigurationPath $this->getContainer()->getParameter('kernel.root_dir').'/config/config.yml';
  293.         $output->writeln(sprintf(
  294.             '> Importing the bundle\'s %s from the <info>%s</info> file',
  295.             $bundle->getServicesConfigurationFilename(),
  296.             $this->makePathRelative($targetConfigurationPath)
  297.         ));
  298.         $manipulator = new ConfigurationManipulator($targetConfigurationPath);
  299.         try {
  300.             $manipulator->addResource($bundle);
  301.         } catch (\RuntimeException $e) {
  302.             return [
  303.                 sprintf('- Import the bundle\'s "%s" resource in the app\'s main configuration file:'$bundle->getServicesConfigurationFilename()),
  304.                 '',
  305.                 $manipulator->getImportCode($bundle),
  306.                 '',
  307.             ];
  308.         }
  309.     }
  310.     /**
  311.      * Creates the Bundle object based on the user's (non-interactive) input.
  312.      *
  313.      * @param InputInterface $input
  314.      *
  315.      * @return Bundle
  316.      */
  317.     protected function createBundleObject(InputInterface $input)
  318.     {
  319.         foreach (['namespace''dir'] as $option) {
  320.             if (null === $input->getOption($option)) {
  321.                 throw new \RuntimeException(sprintf('The "%s" option must be provided.'$option));
  322.             }
  323.         }
  324.         $shared $input->getOption('shared');
  325.         $namespace Validators::validateBundleNamespace($input->getOption('namespace'), $shared);
  326.         if (!$bundleName $input->getOption('bundle-name')) {
  327.             $bundleName strtr($namespace, ['\\' => '']);
  328.         }
  329.         $bundleName Validators::validateBundleName($bundleName);
  330.         $dir $input->getOption('dir');
  331.         if (null === $input->getOption('format')) {
  332.             $input->setOption('format''annotation');
  333.         }
  334.         $format Validators::validateFormat($input->getOption('format'));
  335.         // an assumption that the kernel root dir is in a directory (like app/)
  336.         $projectRootDirectory $this->getContainer()->getParameter('kernel.root_dir').'/..';
  337.         if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) {
  338.             $dir $projectRootDirectory.'/'.$dir;
  339.         }
  340.         // add trailing / if necessary
  341.         $dir '/' === substr($dir, -11) ? $dir $dir.'/';
  342.         $bundle = new Bundle(
  343.             $namespace,
  344.             $bundleName,
  345.             $dir,
  346.             $format,
  347.             $shared
  348.         );
  349.         // not shared - put the tests in the root
  350.         if (!$shared) {
  351.             $testsDir $projectRootDirectory.'/tests/'.$bundleName;
  352.             $bundle->setTestsDirectory($testsDir);
  353.         }
  354.         return $bundle;
  355.     }
  356.     protected function createGenerator()
  357.     {
  358.         return new BundleGenerator();
  359.     }
  360. }