1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Symfony package. |
5
|
|
|
* |
6
|
|
|
* (c) Fabien Potencier <[email protected]> |
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 Pgs\RestfonyBundle\Command; |
13
|
|
|
|
14
|
|
|
use Pgs\RestfonyBundle\Generator\DoctrineCrudGenerator; |
15
|
|
|
use Pgs\RestfonyBundle\Generator\DoctrineFormGenerator; |
16
|
|
|
use Pgs\RestfonyBundle\Generator\DoctrineManagerGenerator; |
17
|
|
|
use Pgs\RestfonyBundle\Generator\DoctrineRepositoryGenerator; |
18
|
|
|
use Pgs\RestfonyBundle\Generator\DoctrineSerializationConfigGenerator; |
19
|
|
|
use Pgs\RestfonyBundle\Manipulator\RestConfigManipulator; |
20
|
|
|
use Pgs\RestfonyBundle\Manipulator\RoutingManipulator; |
21
|
|
|
use Sensio\Bundle\GeneratorBundle\Command\Validators; |
22
|
|
|
use Symfony\Component\Console\Input\InputOption; |
23
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
24
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
25
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
26
|
|
|
use Symfony\Component\HttpKernel\Bundle\BundleInterface; |
27
|
|
|
use Symfony\Component\Console\Question\Question; |
28
|
|
|
use Symfony\Component\Console\Question\ConfirmationQuestion; |
29
|
|
|
use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Generates a Restful CRUD for a Doctrine entity. |
33
|
|
|
* |
34
|
|
|
* @author Lech Groblewicz <[email protected]> |
35
|
|
|
*/ |
36
|
|
|
class GenerateCrudCommand extends GeneratorCommand |
37
|
|
|
{ |
38
|
|
|
protected $serializationConfigGenerator; |
39
|
|
|
protected $managerGenerator; |
40
|
|
|
protected $repositoryGenerator; |
41
|
|
|
protected $entity; |
42
|
|
|
protected $bundle; |
43
|
|
|
private $formGenerator; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @see Command |
47
|
|
|
*/ |
48
|
|
|
protected function configure() |
49
|
|
|
{ |
50
|
|
|
$this |
51
|
|
|
->setDefinition(array( |
52
|
|
|
new InputArgument('entity', InputArgument::OPTIONAL, 'The entity class name to initialize (shortcut notation)'), |
53
|
|
|
new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), |
54
|
|
|
new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'), |
55
|
|
|
new InputOption('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'), |
56
|
|
|
new InputOption('format', '', InputOption::VALUE_REQUIRED, 'Use the format for configuration files (php, xml, yml, or annotation)', 'annotation'), |
57
|
|
|
new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Do not stop the generation if crud controller already exist, thus overwriting all generated files'), |
58
|
|
|
)) |
59
|
|
|
->setDescription('Generates a CRUD based on a Doctrine entity') |
60
|
|
|
->setHelp(<<<EOT |
61
|
|
|
The <info>pgs:generate:crud</info> command generates a CRUD based on a Doctrine entity. |
62
|
|
|
|
63
|
|
|
The default command only generates the list and show actions. |
64
|
|
|
|
65
|
|
|
<info>php app/console pgs:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin</info> |
66
|
|
|
|
67
|
|
|
Using the --with-write option allows to generate the new, edit and delete actions. |
68
|
|
|
|
69
|
|
|
<info>php app/console pgs:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write</info> |
70
|
|
|
|
71
|
|
|
Every generated file is based on a template. There are default templates but they can be overridden by placing custom templates in one of the following locations, by order of priority: |
72
|
|
|
|
73
|
|
|
<info>BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/crud |
74
|
|
|
APP_PATH/Resources/SensioGeneratorBundle/skeleton/crud</info> |
75
|
|
|
|
76
|
|
|
And |
77
|
|
|
|
78
|
|
|
<info>__bundle_path__/Resources/SensioGeneratorBundle/skeleton/form |
79
|
|
|
__project_root__/app/Resources/SensioGeneratorBundle/skeleton/form</info> |
80
|
|
|
|
81
|
|
|
You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton |
82
|
|
|
in order to know the file structure of the skeleton |
83
|
|
|
EOT |
84
|
|
|
) |
85
|
|
|
->setName('pgs:generate:crud') |
86
|
|
|
; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @see Command |
91
|
|
|
* |
92
|
|
|
* @param InputInterface $input |
93
|
|
|
* @param OutputInterface $output |
94
|
|
|
* |
95
|
|
|
* @return int|null |
96
|
|
|
*/ |
97
|
|
|
protected function execute(InputInterface $input, OutputInterface $output) |
98
|
|
|
{ |
99
|
|
|
$questionHelper = $this->getQuestionHelper(); |
100
|
|
|
|
101
|
|
View Code Duplication |
if ($input->isInteractive()) { |
|
|
|
|
102
|
|
|
$question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); |
103
|
|
|
if (!$questionHelper->ask($input, $output, $question)) { |
104
|
|
|
$output->writeln('<error>Command aborted</error>'); |
105
|
|
|
|
106
|
|
|
return 1; |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$entity = Validators::validateEntityName($input->getOption('entity')); |
111
|
|
|
list($bundle, $entity) = $this->parseShortcutNotation($entity); |
112
|
|
|
$this->entity = $entity; |
113
|
|
|
|
114
|
|
|
$format = Validators::validateFormat($input->getOption('format')); |
115
|
|
|
$prefix = $this->getRoutePrefix($input, $entity); |
116
|
|
|
$withWrite = $input->getOption('with-write'); |
117
|
|
|
$forceOverwrite = $input->getOption('overwrite'); |
118
|
|
|
|
119
|
|
|
$questionHelper->writeSection($output, 'CRUD generation'); |
120
|
|
|
|
121
|
|
|
$entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; |
122
|
|
|
$metadata = $this->getEntityMetadata($entityClass); |
123
|
|
|
$bundle = $this->getContainer()->get('kernel')->getBundle($bundle); |
124
|
|
|
$this->bundle = $bundle; |
125
|
|
|
|
126
|
|
|
$generator = $this->getGenerator($bundle); |
127
|
|
|
$generator->generate($bundle, $entity, $metadata[0], $withWrite, $forceOverwrite); |
128
|
|
|
|
129
|
|
|
$output->writeln('Generating the CRUD code: <info>OK</info>'); |
130
|
|
|
|
131
|
|
|
$errors = array(); |
132
|
|
|
$runner = $questionHelper->getRunner($output, $errors); |
133
|
|
|
|
134
|
|
|
// form |
135
|
|
|
$output->write('Generating the Form code: '); |
136
|
|
|
if ($this->generateForm($bundle, $entity, $metadata, $forceOverwrite)) { |
137
|
|
|
$output->writeln('<info>OK</info>'); |
138
|
|
|
} else { |
139
|
|
|
$output->writeln('<comment>Already exists, skipping</comment>'); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$output->write('Generating the Repository code: '); |
143
|
|
|
if ($this->generateRepository($bundle, $entity, $forceOverwrite)) { |
144
|
|
|
$output->writeln('<info>OK</info>'); |
145
|
|
|
} else { |
146
|
|
|
$output->writeln('<comment>Already exists, skipping</comment>'); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
$output->write('Generating the Manager code: '); |
150
|
|
|
if ($this->generateManager($bundle, $entity, $forceOverwrite)) { |
151
|
|
|
$output->writeln('<info>OK</info>'); |
152
|
|
|
} else { |
153
|
|
|
$output->writeln('<comment>Already exists, skipping</comment>'); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$runner($this->updateRouting($questionHelper, $input, $output, $bundle, $format, $entity, $prefix)); |
157
|
|
|
|
158
|
|
|
$output->write('Generating the serialization config: '); |
159
|
|
|
if ($this->generateSerializationConfig($bundle, $entity, $metadata, $forceOverwrite)) { |
160
|
|
|
$output->writeln('<info>OK</info>'); |
161
|
|
|
} else { |
162
|
|
|
$output->writeln('<comment>Already exists, skipping</comment>'); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$runner($this->updateRestRouting($questionHelper, $input, $output, $bundle, $entity, $metadata)); |
166
|
|
|
|
167
|
|
|
$questionHelper->writeGeneratorSummary($output, $errors); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
protected function interact(InputInterface $input, OutputInterface $output) |
171
|
|
|
{ |
172
|
|
|
$questionHelper = $this->getQuestionHelper(); |
173
|
|
|
$questionHelper->writeSection($output, 'Welcome to the Doctrine2 CRUD generator'); |
174
|
|
|
|
175
|
|
|
// namespace |
176
|
|
|
$output->writeln(array( |
177
|
|
|
'', |
178
|
|
|
'This command helps you generate CRUD controllers and templates.', |
179
|
|
|
'', |
180
|
|
|
'First, you need to give the entity for which you want to generate a CRUD.', |
181
|
|
|
'You can give an entity that does not exist yet and the wizard will help', |
182
|
|
|
'you defining it.', |
183
|
|
|
'', |
184
|
|
|
'You must use the shortcut notation like <comment>AcmeBlogBundle:Post</comment>.', |
185
|
|
|
'', |
186
|
|
|
)); |
187
|
|
|
|
188
|
|
|
if ($input->hasArgument('entity') && $input->getArgument('entity') !== '') { |
189
|
|
|
$input->setOption('entity', $input->getArgument('entity')); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$question = new Question($questionHelper->getQuestion('The Entity shortcut name', $input->getOption('entity')), $input->getOption('entity')); |
193
|
|
|
$question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName')); |
194
|
|
|
$entity = $questionHelper->ask($input, $output, $question); |
195
|
|
|
$input->setOption('entity', $entity); |
196
|
|
|
list($bundle, $entity) = $this->parseShortcutNotation($entity); |
197
|
|
|
|
198
|
|
|
// write? |
199
|
|
|
$withWrite = (bool) $input->getOption('with-write'); |
200
|
|
|
|
201
|
|
|
$output->writeln(array( |
202
|
|
|
'', |
203
|
|
|
'By default, the generator creates two actions: list and show.', |
204
|
|
|
'You can also ask it to generate "write" actions: new, update, and delete.', |
205
|
|
|
'', |
206
|
|
|
)); |
207
|
|
|
$question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?', $withWrite), $withWrite); |
208
|
|
|
|
209
|
|
|
$withWrite = $questionHelper->ask($input, $output, $question); |
210
|
|
|
$input->setOption('with-write', $withWrite); |
211
|
|
|
|
212
|
|
|
// summary |
213
|
|
|
$output->writeln(array( |
214
|
|
|
'', |
215
|
|
|
$this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), |
|
|
|
|
216
|
|
|
'', |
217
|
|
|
sprintf("You are going to generate a REST CRUD controller for \"<info>%s:%s</info>\"", $bundle, $entity), |
218
|
|
|
'', |
219
|
|
|
)); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Tries to generate forms if they don't exist yet and if we need write operations on entities. |
224
|
|
|
*/ |
225
|
|
View Code Duplication |
protected function generateSerializationConfig($bundle, $entity, $metadata, $forceOverwrite) |
|
|
|
|
226
|
|
|
{ |
227
|
|
|
try { |
228
|
|
|
$this->getSerializationConfigGenerator($bundle, $entity)->generate($metadata[0], $forceOverwrite); |
229
|
|
|
} catch (\RuntimeException $e) { |
230
|
|
|
return false; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
return true; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Tries to generate forms if they don't exist yet and if we need write operations on entities. |
238
|
|
|
*/ |
239
|
|
View Code Duplication |
protected function generateForm($bundle, $entity, $metadata, $forceOverwrite) |
|
|
|
|
240
|
|
|
{ |
241
|
|
|
try { |
242
|
|
|
$this->getFormGenerator($bundle, $entity, $metadata[0])->generate($forceOverwrite); |
243
|
|
|
} catch (\RuntimeException $e) { |
244
|
|
|
return false; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
return true; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
protected function generateManager($bundle, $entity, $forceOverwrite) |
251
|
|
|
{ |
252
|
|
|
try { |
253
|
|
|
$this->getManagerGenerator($bundle, $entity)->generate($forceOverwrite); |
254
|
|
|
} catch (\RuntimeException $e) { |
255
|
|
|
return false; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
return true; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
protected function generateRepository($bundle, $entity, $forceOverwrite) |
262
|
|
|
{ |
263
|
|
|
try { |
264
|
|
|
$this->getRepositoryGenerator($bundle, $entity)->generate($forceOverwrite); |
265
|
|
|
} catch (\RuntimeException $e) { |
266
|
|
|
return false; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
return true; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
protected function updateRestRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, BundleInterface $bundle, $entity, $metadata) |
|
|
|
|
273
|
|
|
{ |
274
|
|
|
$output->write('Importing REST config: '); |
275
|
|
|
$this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); |
276
|
|
|
$config = new RestConfigManipulator($this->getContainer()->get('kernel')->getRootDir().'/config/rest.yml'); |
277
|
|
|
$config->addResource($bundle->getNamespace(), $entity, $metadata); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
protected function updateRouting(QuestionHelper $questionHelper, InputInterface $input, OutputInterface $output, BundleInterface $bundle, $format, $entity, $prefix) |
281
|
|
|
{ |
282
|
|
|
$auto = true; |
283
|
|
View Code Duplication |
if ($input->isInteractive()) { |
|
|
|
|
284
|
|
|
$question = new ConfirmationQuestion($questionHelper->getQuestion('Confirm automatic update of the Routing', 'yes', '?'), true); |
285
|
|
|
$auto = $questionHelper->ask($input, $output, $question); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
$output->write('Importing the CRUD routes: '); |
289
|
|
|
$this->getContainer()->get('filesystem')->mkdir($bundle->getPath().'/Resources/config/'); |
290
|
|
|
$routing = new RoutingManipulator($bundle->getPath().'/Resources/config/rest_routing.yml'); |
291
|
|
|
|
292
|
|
|
$resourceAdded = false; |
293
|
|
|
|
294
|
|
|
if ($auto) { |
295
|
|
|
$resourceAdded = $routing->addResource($bundle->getName(), '/'.$prefix); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
if (!$resourceAdded) { |
299
|
|
|
$help = sprintf(" <comment>resource: \"@%s/Resources/config/routing/%s.%s\"</comment>\n", $bundle->getName(), strtolower(str_replace('\\', '_', $entity)), $format); |
300
|
|
|
$help .= sprintf(" <comment>prefix: /%s</comment>\n", $prefix); |
301
|
|
|
|
302
|
|
|
return array( |
303
|
|
|
'- Import the bundle\'s routing resource in the bundle routing file', |
304
|
|
|
sprintf(' (%s).', $bundle->getPath().'/Resources/config/rest_routing.yml'), |
305
|
|
|
'', |
306
|
|
|
sprintf(' <comment>%s:</comment>', $bundle->getName().('' !== $prefix ? '_'.str_replace('/', '_', $prefix) : '')), |
307
|
|
|
$help, |
308
|
|
|
'', |
309
|
|
|
); |
310
|
|
|
} |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
protected function getRoutePrefix(InputInterface $input, $entity) |
314
|
|
|
{ |
315
|
|
|
$prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity)); |
316
|
|
|
|
317
|
|
|
if ($prefix && '/' === $prefix[0]) { |
318
|
|
|
$prefix = substr($prefix, 1); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
return $prefix; |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
protected function createGenerator() |
325
|
|
|
{ |
326
|
|
|
return new DoctrineCrudGenerator($this->getContainer()->get('filesystem'), $this->bundle, $this->entity); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
View Code Duplication |
protected function getFormGenerator(BundleInterface $bundle, $entity, $metadata) |
|
|
|
|
330
|
|
|
{ |
331
|
|
|
if (null === $this->formGenerator) { |
332
|
|
|
$this->formGenerator = new DoctrineFormGenerator($bundle, $entity, $metadata); |
333
|
|
|
$this->formGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
return $this->formGenerator; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
View Code Duplication |
protected function getSerializationConfigGenerator(BundleInterface $bundle, $entity) |
|
|
|
|
340
|
|
|
{ |
341
|
|
|
if (null === $this->serializationConfigGenerator) { |
342
|
|
|
$this->serializationConfigGenerator = new DoctrineSerializationConfigGenerator($bundle, $entity); |
343
|
|
|
$this->serializationConfigGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); |
344
|
|
|
} |
345
|
|
|
|
346
|
|
|
return $this->serializationConfigGenerator; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
public function setFormGenerator(DoctrineFormGenerator $formGenerator) |
350
|
|
|
{ |
351
|
|
|
$this->formGenerator = $formGenerator; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
View Code Duplication |
protected function getManagerGenerator(BundleInterface $bundle, $entity) |
|
|
|
|
355
|
|
|
{ |
356
|
|
|
if (null === $this->managerGenerator) { |
357
|
|
|
$this->managerGenerator = new DoctrineManagerGenerator($bundle, $entity); |
358
|
|
|
$this->managerGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
return $this->managerGenerator; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
View Code Duplication |
protected function getRepositoryGenerator(BundleInterface $bundle, $entity) |
|
|
|
|
365
|
|
|
{ |
366
|
|
|
if (null === $this->repositoryGenerator) { |
367
|
|
|
$this->repositoryGenerator = new DoctrineRepositoryGenerator($bundle, $entity); |
368
|
|
|
$this->repositoryGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
return $this->repositoryGenerator; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.