1
|
|
|
<?php |
2
|
|
|
namespace keeko\tools\command; |
3
|
|
|
|
4
|
|
|
use gossi\codegen\model\PhpClass; |
5
|
|
|
use keeko\tools\generator\GeneratorFactory; |
6
|
|
|
use keeko\tools\generator\response\base\ModelResponseTraitGenerator; |
7
|
|
|
use keeko\tools\generator\response\BlankHtmlResponseGenerator; |
8
|
|
|
use keeko\tools\generator\response\BlankJsonResponseGenerator; |
9
|
|
|
use keeko\tools\generator\response\DumpJsonResponseGenerator; |
10
|
|
|
use keeko\tools\generator\response\TwigHtmlResponseGenerator; |
11
|
|
|
use keeko\tools\helpers\QuestionHelperTrait; |
12
|
|
|
use phootwork\collection\Set; |
13
|
|
|
use phootwork\file\File; |
14
|
|
|
use Symfony\Component\Console\Input\InputArgument; |
15
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
16
|
|
|
use Symfony\Component\Console\Input\InputOption; |
17
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
18
|
|
|
use Symfony\Component\Console\Question\ConfirmationQuestion; |
19
|
|
|
use Symfony\Component\Console\Question\Question; |
20
|
|
|
use phootwork\lang\Text; |
21
|
|
|
use keeko\tools\generator\response\ToManyRelationshipJsonResponseGenerator; |
22
|
|
|
use keeko\tools\generator\response\ToOneRelationshipJsonResponseGenerator; |
23
|
|
|
|
24
|
20 |
|
class GenerateResponseCommand extends AbstractGenerateCommand { |
25
|
20 |
|
|
26
|
20 |
|
use QuestionHelperTrait; |
27
|
20 |
|
|
28
|
20 |
|
protected $traits; |
29
|
20 |
|
|
30
|
20 |
|
protected function configure() { |
31
|
|
|
$this->traits = new Set(); |
32
|
20 |
|
|
33
|
20 |
|
$this |
34
|
20 |
|
->setName('generate:response') |
35
|
20 |
|
->setDescription('Generates code for a response') |
36
|
20 |
|
->addArgument( |
37
|
20 |
|
'name', |
38
|
|
|
InputArgument::OPTIONAL, |
39
|
20 |
|
'The name of the action, which should be generated. Typically in the form %nomen%-%verb% (e.g. user-create)' |
40
|
20 |
|
) |
41
|
20 |
|
->addOption( |
42
|
20 |
|
'format', |
43
|
20 |
|
'', |
44
|
20 |
|
InputOption::VALUE_OPTIONAL, |
45
|
1 |
|
'The response format to create', |
46
|
20 |
|
'json' |
47
|
|
|
) |
48
|
|
|
->addOption( |
49
|
20 |
|
'template', |
50
|
|
|
'', |
51
|
20 |
|
InputOption::VALUE_OPTIONAL, |
52
|
20 |
|
'The template for the body method (blank or twig)', |
53
|
|
|
'blank' |
54
|
|
|
) |
55
|
|
|
; |
56
|
|
|
|
57
|
|
|
$this->configureGenerateOptions(); |
58
|
7 |
|
|
59
|
7 |
|
parent::configure(); |
60
|
7 |
|
} |
61
|
1 |
|
|
62
|
|
|
/** |
63
|
6 |
|
* Checks whether actions can be generated at all by reading composer.json and verify |
64
|
|
|
* all required information are available |
65
|
|
|
*/ |
66
|
|
|
private function preCheck() { |
67
|
|
|
$module = $this->packageService->getModule(); |
68
|
|
|
if ($module === null || count($module->getActionNames()) == 0) { |
69
|
|
|
throw new \DomainException('No action definition found in composer.json - please run `keeko generate:action`.'); |
70
|
|
|
} |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
protected function interact(InputInterface $input, OutputInterface $output) { |
74
|
|
|
$this->preCheck(); |
75
|
|
|
|
76
|
|
|
// check if the dialog can be skipped |
77
|
|
|
$name = $input->getArgument('name'); |
78
|
|
|
$specificAction = false; |
79
|
|
|
|
80
|
|
|
if ($name === null) { |
81
|
|
|
$specificQuestion = new ConfirmationQuestion('Do you want to generate a response for a specific action?'); |
82
|
|
|
$specificAction = $this->askConfirmation($specificQuestion); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
// ask which action |
86
|
|
|
if ($specificAction) { |
87
|
|
|
$names = []; |
88
|
|
|
$module = $this->packageService->getModule(); |
89
|
|
|
foreach ($module->getActionNames() as $name) { |
90
|
|
|
$names[] = $name; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
$actionQuestion = new Question('Which action'); |
94
|
|
|
$actionQuestion->setAutocompleterValues($names); |
95
|
|
|
$name = $this->askQuestion($actionQuestion); |
96
|
|
|
$input->setArgument('name', $name); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
|
100
|
|
|
// ask which format |
101
|
|
|
$formatQuestion = new Question('Which format', 'json'); |
102
|
|
|
$formatQuestion->setAutocompleterValues(['json', 'html']); |
103
|
|
|
$format = $this->askQuestion($formatQuestion); |
104
|
|
|
$input->setOption('format', $format); |
105
|
7 |
|
|
106
|
7 |
|
// ask which template |
107
|
|
|
$templates = [ |
108
|
6 |
|
'html' => ['twig', 'blank'], |
109
|
|
|
'json' => ['dump', 'blank'] |
110
|
|
|
]; |
111
|
6 |
|
|
112
|
5 |
|
$suggestions = isset($templates[$format]) ? $templates[$format] : []; |
113
|
4 |
|
$default = count($suggestions) ? $suggestions[0] : ''; |
114
|
|
|
$templateQuestion = new Question('Which template', $default); |
115
|
|
|
$templateQuestion->setAutocompleterValues($suggestions); |
116
|
|
|
$template = $this->askQuestion($templateQuestion); |
117
|
1 |
|
$input->setOption('template', $template); |
118
|
|
|
|
119
|
1 |
|
} |
120
|
1 |
|
|
121
|
1 |
|
protected function execute(InputInterface $input, OutputInterface $output) { |
122
|
|
|
$this->preCheck(); |
123
|
|
|
|
124
|
5 |
|
$name = $input->getArgument('name'); |
125
|
5 |
|
|
126
|
|
|
// only a specific action |
127
|
6 |
|
if ($name) { |
128
|
6 |
|
$this->generateResponse($name); |
129
|
|
|
} |
130
|
6 |
|
|
131
|
1 |
|
// anyway all actions |
132
|
|
|
else { |
133
|
|
|
$actions = $this->packageService->getModule()->getActionNames(); |
134
|
5 |
|
|
135
|
5 |
|
foreach ($actions as $name) { |
136
|
5 |
|
$this->generateResponse($name); |
137
|
5 |
|
} |
138
|
5 |
|
} |
139
|
|
|
|
140
|
5 |
|
$this->packageService->savePackage(); |
141
|
5 |
|
} |
142
|
5 |
|
|
143
|
|
|
protected function generateResponse($actionName) { |
144
|
|
|
$this->logger->info('Generate Response: ' . $actionName); |
145
|
5 |
|
$module = $this->packageService->getModule(); |
146
|
5 |
|
|
147
|
5 |
|
if (!$module->hasAction($actionName)) { |
148
|
|
|
throw new \RuntimeException(sprintf('action (%s) not found', $actionName)); |
149
|
|
|
} |
150
|
5 |
|
|
151
|
2 |
|
$input = $this->io->getInput(); |
152
|
2 |
|
$format = $input->getOption('format'); |
153
|
|
|
$template = $input->getOption('template'); |
154
|
|
|
|
155
|
4 |
|
// check if relationship response |
156
|
2 |
|
if (Text::create($actionName)->contains('relationship') && $format == 'json') { |
157
|
2 |
|
return $this->generateRelationshipResponse($actionName); |
158
|
|
|
} |
159
|
|
|
|
160
|
2 |
|
$action = $module->getAction($actionName); |
161
|
1 |
|
$modelName = $this->modelService->getModelNameByAction($action); |
162
|
1 |
|
|
163
|
|
|
if (!$action->hasResponse($format)) { |
164
|
|
|
$action->setResponse($format, str_replace(['Action', 'action'], [ucwords($format) . 'Response', 'response'], $action->getClass())); |
165
|
1 |
|
} |
166
|
1 |
|
|
167
|
1 |
|
// find generator |
168
|
|
|
$overwrite = false; |
169
|
|
|
$generator = null; |
170
|
5 |
|
$type = $this->packageService->getActionType($actionName, $modelName); |
171
|
|
|
$isModel = $type && $this->modelService->isModelAction($action); |
172
|
5 |
|
|
173
|
|
|
// model given and format is json |
174
|
|
|
if ($isModel && $format == 'json') { |
175
|
5 |
|
$generator = GeneratorFactory::createJsonResponseGenerator($type, $this->service); |
176
|
2 |
|
} |
177
|
2 |
|
|
178
|
2 |
|
// json + dump |
179
|
|
|
else if ($format == 'json' && $template == 'dump') { |
180
|
2 |
|
$generator = new DumpJsonResponseGenerator($this->service); |
181
|
2 |
|
} |
182
|
2 |
|
|
183
|
2 |
|
// blank json |
184
|
2 |
|
else if ($format == 'json') { |
185
|
|
|
$generator = new BlankJsonResponseGenerator($this->service); |
186
|
|
|
} |
187
|
5 |
|
|
188
|
5 |
|
// html + twig |
189
|
5 |
|
else if ($format == 'html' && $template == 'twig') { |
190
|
|
|
$generator = new TwigHtmlResponseGenerator($this->service); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
// blank html as default |
194
|
|
|
else if ($format == 'html') { |
195
|
|
|
$generator = new BlankHtmlResponseGenerator($this->service); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
// run generation, if generator was chosen |
199
|
|
|
if ($generator !== null) { |
200
|
|
|
/* @var $class PhpClass */ |
201
|
|
|
$class = $generator->generate($action); |
202
|
|
|
|
203
|
|
|
// generate json trait |
|
|
|
|
204
|
|
|
// if ($isModel && $format === 'json') { |
205
|
|
|
// $generator = new ModelResponseTraitGenerator($this->service); |
206
|
|
|
// $trait = $generator->generate($action); |
207
|
|
|
|
208
|
|
|
// if (!$class->hasTrait($trait)) { |
|
|
|
|
209
|
|
|
// $class->addTrait($trait); |
210
|
|
|
// $overwrite = true; |
211
|
|
|
// } |
212
|
|
|
|
213
|
|
|
// if (!$this->traits->contains($trait->getName())) { |
|
|
|
|
214
|
|
|
// $this->codegenService->dumpStruct($trait, true); |
215
|
|
|
// $this->traits->add($trait->getName()); |
216
|
|
|
// } |
217
|
|
|
// } |
218
|
|
|
|
219
|
|
|
// write to file |
220
|
|
|
$file = new File($this->codegenService->getFilename($class)); |
221
|
|
|
if (!$file->exists()) { |
222
|
|
|
$overwrite = true; |
223
|
|
|
} |
224
|
|
|
$overwrite = $overwrite || $input->getOption('force'); |
225
|
|
|
$this->codegenService->dumpStruct($class, $overwrite); |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
protected function generateRelationshipResponse($actionName) { |
230
|
|
|
$module = $this->packageService->getModule(); |
231
|
|
|
$action = $module->getAction($actionName); |
232
|
|
|
$prefix = substr($actionName, 0, strpos($actionName, 'relationship') + 12); |
233
|
|
|
$readAction = $module->getAction($prefix.'-read'); |
234
|
|
|
|
235
|
|
|
// get modules names |
236
|
|
|
$matches = []; |
237
|
|
|
preg_match('/([a-z_]+)-to-([a-z_]+)-relationship.*/i', $actionName, $matches); |
238
|
|
|
$model = $this->modelService->getModel($matches[1]); |
239
|
|
|
$foreign = $this->modelService->getModel($matches[2]); |
240
|
|
|
|
241
|
|
|
// response class name |
242
|
|
|
$response = sprintf('%s\\response\\%s%sJsonResponse', |
243
|
|
|
$this->modelService->getRootNamespace(), |
244
|
|
|
$model->getPhpName(), |
245
|
|
|
$foreign->getPhpName() |
246
|
|
|
); |
247
|
|
|
|
248
|
|
|
$many = $module->hasAction($prefix . '-read') |
249
|
|
|
&& $module->hasAction($prefix . '-update') |
250
|
|
|
&& $module->hasAction($prefix . '-add') |
251
|
|
|
&& $module->hasAction($prefix . '-remove') |
252
|
|
|
; |
253
|
|
|
$single = $module->hasAction($prefix . '-read') |
254
|
|
|
&& $module->hasAction($prefix . '-update') |
255
|
|
|
&& !$many |
256
|
|
|
; |
257
|
|
|
|
258
|
|
|
$generator = null; |
259
|
|
|
if ($many) { |
260
|
|
|
$generator = new ToManyRelationshipJsonResponseGenerator($this->service, $model, $foreign); |
|
|
|
|
261
|
|
|
} else if ($single) { |
262
|
|
|
$generator = new ToOneRelationshipJsonResponseGenerator($this->service, $model, $foreign); |
|
|
|
|
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
if ($generator !== null) { |
266
|
|
|
$action->setResponse('json', $response); |
267
|
|
|
$response = $generator->generate($readAction); |
268
|
|
|
$this->codegenService->dumpStruct($response, true); |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
} |
273
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.