|
0
|
1 |
<?php |
|
|
2 |
|
|
|
3 |
/* |
|
|
4 |
* This file is part of the Symfony package. |
|
|
5 |
* |
|
|
6 |
* (c) Fabien Potencier <fabien@symfony.com> |
|
|
7 |
* |
|
|
8 |
* For the full copyright and license information, please view the LICENSE |
|
|
9 |
* file that was distributed with this source code. |
|
|
10 |
*/ |
|
|
11 |
|
|
|
12 |
namespace Sensio\Bundle\GeneratorBundle\Command; |
|
|
13 |
|
|
|
14 |
use Symfony\Component\Console\Input\InputOption; |
|
|
15 |
use Symfony\Component\Console\Input\InputInterface; |
|
|
16 |
use Symfony\Component\Console\Output\OutputInterface; |
|
|
17 |
use Symfony\Component\Console\Output\Output; |
|
|
18 |
use Symfony\Component\HttpKernel\KernelInterface; |
|
|
19 |
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; |
|
|
20 |
use Sensio\Bundle\GeneratorBundle\Generator\BundleGenerator; |
|
|
21 |
use Sensio\Bundle\GeneratorBundle\Manipulator\KernelManipulator; |
|
|
22 |
use Sensio\Bundle\GeneratorBundle\Manipulator\RoutingManipulator; |
|
|
23 |
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper; |
|
|
24 |
|
|
|
25 |
/** |
|
|
26 |
* Generates bundles. |
|
|
27 |
* |
|
|
28 |
* @author Fabien Potencier <fabien@symfony.com> |
|
|
29 |
*/ |
|
|
30 |
class GenerateBundleCommand extends ContainerAwareCommand |
|
|
31 |
{ |
|
|
32 |
private $generator; |
|
|
33 |
|
|
|
34 |
/** |
|
|
35 |
* @see Command |
|
|
36 |
*/ |
|
|
37 |
protected function configure() |
|
|
38 |
{ |
|
|
39 |
$this |
|
|
40 |
->setDefinition(array( |
|
|
41 |
new InputOption('namespace', '', InputOption::VALUE_REQUIRED, 'The namespace of the bundle to create'), |
|
|
42 |
new InputOption('dir', '', InputOption::VALUE_REQUIRED, 'The directory where to create the bundle'), |
|
|
43 |
new InputOption('bundle-name', '', InputOption::VALUE_REQUIRED, 'The optional bundle name'), |
|
|
44 |
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'), |
|
|
45 |
new InputOption('structure', '', InputOption::VALUE_NONE, 'Whether to generate the whole directory structure'), |
|
|
46 |
)) |
|
|
47 |
->setDescription('Generates a bundle') |
|
|
48 |
->setHelp(<<<EOT |
|
|
49 |
The <info>generate:bundle</info> command helps you generates new bundles. |
|
|
50 |
|
|
|
51 |
By default, the command interacts with the developer to tweak the generation. |
|
|
52 |
Any passed option will be used as a default value for the interaction |
|
|
53 |
(<comment>--namespace</comment> is the only one needed if you follow the |
|
|
54 |
conventions): |
|
|
55 |
|
|
|
56 |
<info>php app/console generate:bundle --namespace=Acme/BlogBundle</info> |
|
|
57 |
|
|
|
58 |
Note that you can use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any |
|
|
59 |
problem. |
|
|
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 |
<info>php app/console generate:bundle --namespace=Acme/BlogBundle --dir=src [--bundle-name=...] --no-interaction</info> |
|
|
64 |
|
|
|
65 |
Note that the bundle namespace must end with "Bundle". |
|
|
66 |
EOT |
|
|
67 |
) |
|
|
68 |
->setName('generate:bundle') |
|
|
69 |
; |
|
|
70 |
} |
|
|
71 |
|
|
|
72 |
/** |
|
|
73 |
* @see Command |
|
|
74 |
* |
|
|
75 |
* @throws \InvalidArgumentException When namespace doesn't end with Bundle |
|
|
76 |
* @throws \RuntimeException When bundle can't be executed |
|
|
77 |
*/ |
|
|
78 |
protected function execute(InputInterface $input, OutputInterface $output) |
|
|
79 |
{ |
|
|
80 |
$dialog = $this->getDialogHelper(); |
|
|
81 |
|
|
|
82 |
if ($input->isInteractive()) { |
|
|
83 |
if (!$dialog->askConfirmation($output, $dialog->getQuestion('Do you confirm generation', 'yes', '?'), true)) { |
|
|
84 |
$output->writeln('<error>Command aborted</error>'); |
|
|
85 |
|
|
|
86 |
return 1; |
|
|
87 |
} |
|
|
88 |
} |
|
|
89 |
|
|
|
90 |
foreach (array('namespace', 'dir') as $option) { |
|
|
91 |
if (null === $input->getOption($option)) { |
|
|
92 |
throw new \RuntimeException(sprintf('The "%s" option must be provided.', $option)); |
|
|
93 |
} |
|
|
94 |
} |
|
|
95 |
|
|
|
96 |
$namespace = Validators::validateBundleNamespace($input->getOption('namespace')); |
|
|
97 |
if (!$bundle = $input->getOption('bundle-name')) { |
|
|
98 |
$bundle = strtr($namespace, array('\\' => '')); |
|
|
99 |
} |
|
|
100 |
$bundle = Validators::validateBundleName($bundle); |
|
|
101 |
$dir = Validators::validateTargetDir($input->getOption('dir'), $bundle, $namespace); |
|
|
102 |
$format = Validators::validateFormat($input->getOption('format')); |
|
|
103 |
$structure = $input->getOption('structure'); |
|
|
104 |
|
|
|
105 |
$dialog->writeSection($output, 'Bundle generation'); |
|
|
106 |
|
|
|
107 |
if (!$this->getContainer()->get('filesystem')->isAbsolutePath($dir)) { |
|
|
108 |
$dir = getcwd().'/'.$dir; |
|
|
109 |
} |
|
|
110 |
|
|
|
111 |
$generator = $this->getGenerator(); |
|
|
112 |
$generator->generate($namespace, $bundle, $dir, $format, $structure); |
|
|
113 |
|
|
|
114 |
$output->writeln('Generating the bundle code: <info>OK</info>'); |
|
|
115 |
|
|
|
116 |
$errors = array(); |
|
|
117 |
$runner = $dialog->getRunner($output, $errors); |
|
|
118 |
|
|
|
119 |
// check that the namespace is already autoloaded |
|
|
120 |
$runner($this->checkAutoloader($output, $namespace, $bundle, $dir)); |
|
|
121 |
|
|
|
122 |
// register the bundle in the Kernel class |
|
|
123 |
$runner($this->updateKernel($dialog, $input, $output, $this->getContainer()->get('kernel'), $namespace, $bundle)); |
|
|
124 |
|
|
|
125 |
// routing |
|
|
126 |
$runner($this->updateRouting($dialog, $input, $output, $bundle, $format)); |
|
|
127 |
|
|
|
128 |
$dialog->writeGeneratorSummary($output, $errors); |
|
|
129 |
} |
|
|
130 |
|
|
|
131 |
protected function interact(InputInterface $input, OutputInterface $output) |
|
|
132 |
{ |
|
|
133 |
$dialog = $this->getDialogHelper(); |
|
|
134 |
$dialog->writeSection($output, 'Welcome to the Symfony2 bundle generator'); |
|
|
135 |
|
|
|
136 |
// namespace |
|
|
137 |
$output->writeln(array( |
|
|
138 |
'', |
|
|
139 |
'Your application code must be written in <comment>bundles</comment>. This command helps', |
|
|
140 |
'you generate them easily.', |
|
|
141 |
'', |
|
|
142 |
'Each bundle is hosted under a namespace (like <comment>Acme/Bundle/BlogBundle</comment>).', |
|
|
143 |
'The namespace should begin with a "vendor" name like your company name, your', |
|
|
144 |
'project name, or your client name, followed by one or more optional category', |
|
|
145 |
'sub-namespaces, and it should end with the bundle name itself', |
|
|
146 |
'(which must have <comment>Bundle</comment> as a suffix).', |
|
|
147 |
'', |
|
|
148 |
'Use <comment>/</comment> instead of <comment>\\</comment> for the namespace delimiter to avoid any problem.', |
|
|
149 |
'', |
|
|
150 |
)); |
|
|
151 |
|
|
|
152 |
$namespace = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle namespace', $input->getOption('namespace')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleNamespace'), false, $input->getOption('namespace')); |
|
|
153 |
$input->setOption('namespace', $namespace); |
|
|
154 |
|
|
|
155 |
// bundle name |
|
|
156 |
$bundle = $input->getOption('bundle-name') ?: strtr($namespace, array('\\Bundle\\' => '', '\\' => '')); |
|
|
157 |
$output->writeln(array( |
|
|
158 |
'', |
|
|
159 |
'In your code, a bundle is often referenced by its name. It can be the', |
|
|
160 |
'concatenation of all namespace parts but it\'s really up to you to come', |
|
|
161 |
'up with a unique name (a good practice is to start with the vendor name).', |
|
|
162 |
'Based on the namespace, we suggest <comment>'.$bundle.'</comment>.', |
|
|
163 |
'', |
|
|
164 |
)); |
|
|
165 |
$bundle = $dialog->askAndValidate($output, $dialog->getQuestion('Bundle name', $bundle), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateBundleName'), false, $bundle); |
|
|
166 |
$input->setOption('bundle-name', $bundle); |
|
|
167 |
|
|
|
168 |
// target dir |
|
|
169 |
$dir = $input->getOption('dir') ?: dirname($this->getContainer()->getParameter('kernel.root_dir')).'/src'; |
|
|
170 |
$output->writeln(array( |
|
|
171 |
'', |
|
|
172 |
'The bundle can be generated anywhere. The suggested default directory uses', |
|
|
173 |
'the standard conventions.', |
|
|
174 |
'', |
|
|
175 |
)); |
|
|
176 |
$dir = $dialog->askAndValidate($output, $dialog->getQuestion('Target directory', $dir), function ($dir) use ($bundle, $namespace) { return Validators::validateTargetDir($dir, $bundle, $namespace); }, false, $dir); |
|
|
177 |
$input->setOption('dir', $dir); |
|
|
178 |
|
|
|
179 |
// format |
|
|
180 |
$output->writeln(array( |
|
|
181 |
'', |
|
|
182 |
'Determine the format to use for the generated configuration.', |
|
|
183 |
'', |
|
|
184 |
)); |
|
|
185 |
$format = $dialog->askAndValidate($output, $dialog->getQuestion('Configuration format (yml, xml, php, or annotation)', $input->getOption('format')), array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateFormat'), false, $input->getOption('format')); |
|
|
186 |
$input->setOption('format', $format); |
|
|
187 |
|
|
|
188 |
// optional files to generate |
|
|
189 |
$output->writeln(array( |
|
|
190 |
'', |
|
|
191 |
'To help you getting started faster, the command can generate some', |
|
|
192 |
'code snippets for you.', |
|
|
193 |
'', |
|
|
194 |
)); |
|
|
195 |
|
|
|
196 |
$structure = $input->getOption('structure'); |
|
|
197 |
if (!$structure && $dialog->askConfirmation($output, $dialog->getQuestion('Do you want to generate the whole directory structure', 'no', '?'), false)) { |
|
|
198 |
$structure = true; |
|
|
199 |
} |
|
|
200 |
$input->setOption('structure', $structure); |
|
|
201 |
|
|
|
202 |
// summary |
|
|
203 |
$output->writeln(array( |
|
|
204 |
'', |
|
|
205 |
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), |
|
|
206 |
'', |
|
|
207 |
sprintf("You are going to generate a \"<info>%s\\%s</info>\" bundle\nin \"<info>%s</info>\" using the \"<info>%s</info>\" format.", $namespace, $bundle, $dir, $format), |
|
|
208 |
'', |
|
|
209 |
)); |
|
|
210 |
} |
|
|
211 |
|
|
|
212 |
protected function checkAutoloader(OutputInterface $output, $namespace, $bundle, $dir) |
|
|
213 |
{ |
|
|
214 |
$output->write('Checking that the bundle is autoloaded: '); |
|
|
215 |
if (!class_exists($namespace.'\\'.$bundle)) { |
|
|
216 |
return array( |
|
|
217 |
'- Edit the <comment>app/autoloader.php</comment> file and register the bundle', |
|
|
218 |
' namespace at the top of the <comment>registerNamespaces()</comment> call:', |
|
|
219 |
'', |
|
|
220 |
sprintf('<comment> \'%s\' => \'%s\',</comment>', $namespace, realpath($dir)), |
|
|
221 |
'', |
|
|
222 |
); |
|
|
223 |
} |
|
|
224 |
} |
|
|
225 |
|
|
|
226 |
protected function updateKernel($dialog, InputInterface $input, OutputInterface $output, KernelInterface $kernel, $namespace, $bundle) |
|
|
227 |
{ |
|
|
228 |
$auto = true; |
|
|
229 |
if ($input->isInteractive()) { |
|
|
230 |
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of your Kernel', 'yes', '?'), true); |
|
|
231 |
} |
|
|
232 |
|
|
|
233 |
$output->write('Enabling the bundle inside the Kernel: '); |
|
|
234 |
$manip = new KernelManipulator($kernel); |
|
|
235 |
try { |
|
|
236 |
$ret = $auto ? $manip->addBundle($namespace.'\\'.$bundle) : false; |
|
|
237 |
|
|
|
238 |
if (!$ret) { |
|
|
239 |
$reflected = new \ReflectionObject($kernel); |
|
|
240 |
|
|
|
241 |
return array( |
|
|
242 |
sprintf('- Edit <comment>%s</comment>', $reflected->getFilename()), |
|
|
243 |
' and add the following bundle in the <comment>AppKernel::registerBundles()</comment> method:', |
|
|
244 |
'', |
|
|
245 |
sprintf(' <comment>new %s(),</comment>', $namespace.'\\'.$bundle), |
|
|
246 |
'', |
|
|
247 |
); |
|
|
248 |
} |
|
|
249 |
} catch (\RuntimeException $e) { |
|
|
250 |
return array( |
|
|
251 |
sprintf('Bundle <comment>%s</comment> is already defined in <comment>AppKernel::registerBundles()</comment>.', $namespace.'\\'.$bundle), |
|
|
252 |
'', |
|
|
253 |
); |
|
|
254 |
} |
|
|
255 |
} |
|
|
256 |
|
|
|
257 |
protected function updateRouting($dialog, InputInterface $input, OutputInterface $output, $bundle, $format) |
|
|
258 |
{ |
|
|
259 |
$auto = true; |
|
|
260 |
if ($input->isInteractive()) { |
|
|
261 |
$auto = $dialog->askConfirmation($output, $dialog->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); |
|
|
262 |
} |
|
|
263 |
|
|
|
264 |
$output->write('Importing the bundle routing resource: '); |
|
|
265 |
$routing = new RoutingManipulator($this->getContainer()->getParameter('kernel.root_dir').'/config/routing.yml'); |
|
|
266 |
try { |
|
|
267 |
$ret = $auto ? $routing->addResource($bundle, $format) : false; |
|
|
268 |
if (!$ret) { |
|
|
269 |
if ('annotation' === $format) { |
|
|
270 |
$help = sprintf(" <comment>resource: \"@%s/Resources/Controller/\"</comment>\n <comment>type: annotation</comment>", $bundle); |
|
|
271 |
} else { |
|
|
272 |
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing.%s\"</comment>\n", $bundle, $format); |
|
|
273 |
} |
|
|
274 |
$help .= " <comment>prefix: /</comment>\n"; |
|
|
275 |
|
|
|
276 |
return array( |
|
|
277 |
'- Import the bundle\'s routing resource in the app main routing file:', |
|
|
278 |
'', |
|
|
279 |
sprintf(' <comment>%s:</comment>', $bundle), |
|
|
280 |
$help, |
|
|
281 |
'', |
|
|
282 |
); |
|
|
283 |
} |
|
|
284 |
} catch (\RuntimeException $e) { |
|
|
285 |
return array( |
|
|
286 |
sprintf('Bundle <comment>%s</comment> is already imported.', $bundle), |
|
|
287 |
'', |
|
|
288 |
); |
|
|
289 |
} |
|
|
290 |
} |
|
|
291 |
|
|
|
292 |
protected function getGenerator() |
|
|
293 |
{ |
|
|
294 |
if (null === $this->generator) { |
|
|
295 |
$this->generator = new BundleGenerator($this->getContainer()->get('filesystem'), __DIR__.'/../Resources/skeleton/bundle'); |
|
|
296 |
} |
|
|
297 |
|
|
|
298 |
return $this->generator; |
|
|
299 |
} |
|
|
300 |
|
|
|
301 |
public function setGenerator(BundleGenerator $generator) |
|
|
302 |
{ |
|
|
303 |
$this->generator = $generator; |
|
|
304 |
} |
|
|
305 |
|
|
|
306 |
protected function getDialogHelper() |
|
|
307 |
{ |
|
|
308 |
$dialog = $this->getHelperSet()->get('dialog'); |
|
|
309 |
if (!$dialog || get_class($dialog) !== 'Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper') { |
|
|
310 |
$this->getHelperSet()->set($dialog = new DialogHelper()); |
|
|
311 |
} |
|
|
312 |
|
|
|
313 |
return $dialog; |
|
|
314 |
} |
|
|
315 |
} |