1
|
|
|
<?php |
2
|
|
|
namespace keeko\tools\command; |
3
|
|
|
|
4
|
|
|
use gossi\codegen\model\PhpClass; |
5
|
|
|
use gossi\codegen\model\PhpMethod; |
6
|
|
|
use gossi\codegen\model\PhpProperty; |
7
|
|
|
use gossi\codegen\model\PhpTrait; |
8
|
|
|
use keeko\framework\schema\ActionSchema; |
9
|
|
|
use keeko\framework\utils\NameUtils; |
10
|
|
|
use keeko\tools\generator\action\BlankActionGenerator; |
11
|
|
|
use keeko\tools\generator\action\NoopActionGenerator; |
12
|
|
|
use keeko\tools\generator\action\ToManyRelationshipAddActionGenerator; |
13
|
|
|
use keeko\tools\generator\action\ToManyRelationshipReadActionGenerator; |
14
|
|
|
use keeko\tools\generator\action\ToManyRelationshipRemoveActionGenerator; |
15
|
|
|
use keeko\tools\generator\action\ToManyRelationshipUpdateActionGenerator; |
16
|
|
|
use keeko\tools\generator\action\ToOneRelationshipReadActionGenerator; |
17
|
|
|
use keeko\tools\generator\action\ToOneRelationshipUpdateActionGenerator; |
18
|
|
|
use keeko\tools\generator\GeneratorFactory; |
19
|
|
|
use keeko\tools\generator\SerializerGenerator; |
20
|
|
|
use keeko\tools\helpers\QuestionHelperTrait; |
21
|
|
|
use keeko\tools\utils\NamespaceResolver; |
22
|
|
|
use phootwork\file\File; |
23
|
|
|
use phootwork\lang\Text; |
24
|
|
|
use Propel\Generator\Model\ForeignKey; |
25
|
20 |
|
use Propel\Generator\Model\Table; |
26
|
20 |
|
use Symfony\Component\Console\Input\InputArgument; |
27
|
20 |
|
use Symfony\Component\Console\Input\InputInterface; |
28
|
20 |
|
use Symfony\Component\Console\Input\InputOption; |
29
|
20 |
|
use Symfony\Component\Console\Output\OutputInterface; |
30
|
20 |
|
use Symfony\Component\Console\Question\ConfirmationQuestion; |
31
|
20 |
|
use Symfony\Component\Console\Question\Question; |
32
|
|
|
|
33
|
20 |
|
class GenerateActionCommand extends AbstractGenerateCommand { |
34
|
20 |
|
|
35
|
20 |
|
use QuestionHelperTrait; |
36
|
20 |
|
|
37
|
20 |
|
private $twig; |
38
|
20 |
|
|
39
|
|
|
protected function configure() { |
40
|
20 |
|
$this |
41
|
20 |
|
->setName('generate:action') |
42
|
20 |
|
->setDescription('Generates an action') |
43
|
20 |
|
->addArgument( |
44
|
20 |
|
'name', |
45
|
|
|
InputArgument::OPTIONAL, |
46
|
20 |
|
'The name of the action, which should be generated. Typically in the form %nomen%-%verb% (e.g. user-create)' |
47
|
20 |
|
) |
48
|
20 |
|
->addOption( |
49
|
20 |
|
'classname', |
50
|
20 |
|
'c', |
51
|
|
|
InputOption::VALUE_OPTIONAL, |
52
|
20 |
|
'The main class name (If ommited, class name will be guessed from action name)', |
53
|
20 |
|
null |
54
|
20 |
|
) |
55
|
20 |
|
->addOption( |
56
|
20 |
|
'model', |
57
|
|
|
'm', |
58
|
20 |
|
InputOption::VALUE_OPTIONAL, |
59
|
20 |
|
'The model for which the actions should be generated, when there is no name argument (if ommited all models will be generated)' |
60
|
20 |
|
) |
61
|
20 |
|
->addOption( |
62
|
|
|
'title', |
63
|
20 |
|
'', |
64
|
|
|
InputOption::VALUE_OPTIONAL, |
65
|
|
|
'The title for the generated option' |
66
|
|
|
) |
67
|
|
|
->addOption( |
68
|
|
|
'type', |
69
|
|
|
'', |
70
|
|
|
InputOption::VALUE_OPTIONAL, |
71
|
1 |
|
'The type of this action (list|create|read|update|delete) (if ommited template is guessed from action name)' |
72
|
|
|
)->addOption( |
73
|
20 |
|
'acl', |
74
|
|
|
'', |
75
|
20 |
|
InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, |
76
|
20 |
|
'The acl\s for this action (guest, user and/or admin)' |
77
|
|
|
) |
78
|
|
|
// ->addOption( |
|
|
|
|
79
|
|
|
// 'schema', |
80
|
|
|
// 's', |
81
|
|
|
// InputOption::VALUE_OPTIONAL, |
82
|
10 |
|
// 'Path to the database schema (if ommited, database/schema.xml is used)', |
83
|
10 |
|
// null |
84
|
10 |
|
// ) |
85
|
1 |
|
; |
86
|
4 |
|
|
87
|
9 |
|
$this->configureGenerateOptions(); |
88
|
|
|
|
89
|
|
|
parent::configure(); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
protected function initialize(InputInterface $input, OutputInterface $output) { |
93
|
|
|
parent::initialize($input, $output); |
94
|
|
|
|
95
|
|
|
$loader = new \Twig_Loader_Filesystem($this->service->getConfig()->getTemplateRoot() . '/actions'); |
96
|
|
|
$this->twig = new \Twig_Environment($loader); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Checks whether actions can be generated at all by reading composer.json and verify |
101
|
|
|
* all required information are available |
102
|
|
|
*/ |
103
|
|
|
private function preCheck() { |
104
|
|
|
$module = $this->packageService->getModule(); |
105
|
|
|
if ($module === null) { |
106
|
|
|
throw new \DomainException('No module definition found in composer.json - please run `keeko init`.'); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
protected function interact(InputInterface $input, OutputInterface $output) { |
111
|
|
|
$this->preCheck(); |
112
|
|
|
|
113
|
|
|
// check if the dialog can be skipped |
114
|
|
|
$name = $input->getArgument('name'); |
115
|
|
|
$model = $input->getOption('model'); |
116
|
|
|
|
117
|
|
|
if ($model !== null) { |
118
|
|
|
return; |
119
|
|
|
} else if ($name !== null) { |
120
|
|
|
$generateModel = false; |
121
|
|
|
} else { |
122
|
|
|
$modelQuestion = new ConfirmationQuestion('Do you want to generate an action based off a model?'); |
123
|
|
|
$generateModel = $this->askConfirmation($modelQuestion); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
// ask questions for a model |
127
|
|
|
if ($generateModel /*&& !($this->package->getVendor() === 'keeko' && $this->modelService->isCoreSchema())*/) { |
|
|
|
|
128
|
|
|
|
129
|
|
|
$schema = str_replace(getcwd(), '', $this->modelService->getSchema()); |
130
|
|
|
$allQuestion = new ConfirmationQuestion(sprintf('For all models in the schema (%s)?', $schema)); |
131
|
|
|
$allModels = $this->askConfirmation($allQuestion); |
132
|
|
|
|
133
|
|
|
if (!$allModels) { |
134
|
|
|
$modelQuestion = new Question('Which model'); |
135
|
|
|
$modelQuestion->setAutocompleterValues($this->modelService->getModelNames()); |
136
|
|
|
$model = $this->askQuestion($modelQuestion); |
137
|
|
|
$input->setOption('model', $model); |
138
|
|
|
} |
139
|
|
|
} else if (!$generateModel) { |
140
|
|
|
$action = $this->getAction($name); |
141
|
|
|
|
142
|
|
|
// ask for title |
143
|
|
|
$pkgTitle = $action->getTitle(); |
144
|
|
|
$title = $input->getOption('title'); |
145
|
|
|
if ($title === null && !empty($pkgTitle)) { |
146
|
|
|
$title = $pkgTitle; |
147
|
|
|
} |
148
|
|
|
$titleQuestion = new Question('What\'s the title for your action?', $title); |
149
|
|
|
$title = $this->askQuestion($titleQuestion); |
150
|
|
|
$input->setOption('title', $title); |
151
|
|
|
|
152
|
10 |
|
// ask for classname |
153
|
10 |
|
$pkgClass = $action->getClass(); |
154
|
|
|
$classname = $input->getOption('classname'); |
155
|
|
|
if ($classname === null) { |
156
|
|
|
if (!empty($pkgClass)) { |
157
|
|
|
$classname = $pkgClass; |
158
|
|
|
} else { |
159
|
9 |
|
$classname = $this->guessClassname($name); |
160
|
9 |
|
} |
161
|
|
|
} |
162
|
|
|
$classname = $this->askQuestion(new Question('Classname', $classname)); |
163
|
9 |
|
$input->setOption('classname', $classname); |
164
|
3 |
|
|
165
|
2 |
|
// ask for acl |
166
|
|
|
$acls = $this->getAcl($action); |
167
|
|
|
$aclQuestion = new Question('ACL (comma separated list, with these options: guest, user, admin)', implode(', ', $acls)); |
168
|
6 |
|
$acls = $this->askQuestion($aclQuestion); |
169
|
2 |
|
$input->setOption('acl', $acls); |
170
|
2 |
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
4 |
|
protected function execute(InputInterface $input, OutputInterface $output) { |
174
|
3 |
|
$this->preCheck(); |
175
|
3 |
|
|
176
|
2 |
|
// 1. find out which action(s) to generate |
177
|
2 |
|
// 2. generate the information in the package |
178
|
2 |
|
// 3. generate the code for the action |
179
|
1 |
|
|
180
|
|
|
$name = $input->getArgument('name'); |
181
|
3 |
|
$model = $input->getOption('model'); |
182
|
|
|
|
183
|
|
|
// only a specific action |
184
|
|
|
if ($name) { |
185
|
1 |
|
$this->generateAction($name); |
186
|
1 |
|
} |
187
|
1 |
|
|
188
|
|
|
// create action(s) from a model |
189
|
|
|
else if ($model) { |
190
|
8 |
|
$this->generateModel($model); |
191
|
8 |
|
} |
192
|
|
|
|
193
|
5 |
|
// if this is a core-module, find the related model |
194
|
5 |
|
// else /*if ($this->package->getVendor() == 'keeko' && $this->modelService->isCoreSchema()) */ { |
195
|
5 |
|
// $model = $this->package->getName(); |
196
|
5 |
|
// if ($this->modelService->hasModel($model)) { |
197
|
5 |
|
// $input->setOption('model', $model); |
198
|
1 |
|
// $this->generateModel($model); |
199
|
1 |
|
// } else { |
200
|
4 |
|
// $this->logger->error('Tried to find model on my own, wasn\'t lucky - please provide model with the --model option'); |
201
|
|
|
// } |
202
|
|
|
// } |
203
|
5 |
|
|
204
|
5 |
|
// anyway, generate all |
205
|
5 |
|
else { |
206
|
5 |
|
foreach ($this->modelService->getModels() as $model) { |
207
|
5 |
|
$modelName = $model->getOriginCommonName(); |
208
|
5 |
|
$input->setOption('model', $modelName); |
209
|
4 |
|
$this->generateModel($modelName); |
210
|
5 |
|
} |
211
|
5 |
|
} |
212
|
5 |
|
|
213
|
|
|
$this->packageService->savePackage(); |
214
|
5 |
|
} |
215
|
5 |
|
|
216
|
|
|
private function generateModel($modelName) { |
217
|
4 |
|
$this->logger->info('Generate Action from Model: ' . $modelName); |
218
|
|
|
$input = $this->io->getInput(); |
219
|
4 |
|
$model = $this->modelService->getModel($modelName); |
220
|
3 |
|
$typeDump = $input->getOption('type'); |
221
|
|
|
if ($typeDump !== null) { |
222
|
4 |
|
$types = [$typeDump]; |
223
|
4 |
|
} else { |
224
|
4 |
|
$types = ['create', 'read', 'list', 'update', 'delete']; |
225
|
4 |
|
} |
226
|
4 |
|
|
227
|
|
|
foreach ($types as $type) { |
228
|
|
|
$input->setOption('acl', ['admin']); |
|
|
|
|
229
|
|
|
$input->setOption('type', $type); |
230
|
|
|
$actionName = $modelName . '-' . $type; |
231
|
|
|
|
232
|
|
|
if ($model->isReadOnly() && in_array($type, ['create', 'update', 'delete'])) { |
233
|
|
|
$this->logger->info(sprintf('Skip generate Action (%s), because Model (%s) is read-only', $actionName, $modelName)); |
234
|
|
|
continue; |
235
|
|
|
} |
236
|
|
|
|
237
|
8 |
|
$action = $this->getAction($actionName); |
238
|
8 |
|
if (Text::create($action->getTitle())->isEmpty()) { |
239
|
8 |
|
$action->setTitle($this->getActionTitle($modelName, $type)); |
240
|
|
|
} |
241
|
|
|
$this->generateAction($actionName); |
242
|
8 |
|
} |
243
|
|
|
|
244
|
8 |
|
// prepare model for API usage |
245
|
2 |
|
$this->prepareModelForApi($model); |
|
|
|
|
246
|
2 |
|
|
247
|
|
|
// generate relationship actions |
248
|
8 |
|
if (!$model->isReadOnly()) { |
249
|
1 |
|
$relationships = $this->modelService->getRelationships($model); |
|
|
|
|
250
|
|
|
|
251
|
|
|
// to-one relationships |
252
|
7 |
|
foreach ($relationships['one'] as $one) { |
253
|
2 |
|
$fk = $one['fk']; |
254
|
2 |
|
$this->generateToOneRelationshipAction($model, $fk->getForeignTable(), $fk); |
|
|
|
|
255
|
|
|
} |
256
|
|
|
|
257
|
7 |
|
// to-many relationships |
258
|
4 |
|
foreach ($relationships['many'] as $many) { |
259
|
4 |
|
$fk = $many['fk']; |
260
|
|
|
$cfk = $many['cfk']; |
261
|
|
|
$this->generateToManyRelationshipAction($model, $fk->getForeignTable(), $cfk->getMiddleTable()); |
|
|
|
|
262
|
7 |
|
} |
263
|
7 |
|
} |
264
|
7 |
|
|
265
|
|
|
$input->setOption('type', $typeDump); |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
private function getActionTitle($modelName, $type) { |
269
|
|
|
$name = NameUtils::dasherize($modelName); |
|
|
|
|
270
|
|
|
switch ($type) { |
271
|
7 |
|
case 'list': |
272
|
|
|
return 'List all ' . NameUtils::pluralize($name); |
273
|
|
|
|
274
|
7 |
|
case 'create': |
275
|
7 |
|
case 'read': |
276
|
|
|
case 'update': |
277
|
4 |
|
case 'delete': |
278
|
4 |
|
return ucfirst($type) . 's ' . (in_array($name[0], ['a', 'e', 'i', 'o', 'u']) ? 'an' : 'a') . ' ' . $name; |
279
|
4 |
|
} |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Generates an action. |
285
|
|
|
* |
286
|
|
|
* @param string $actionName |
287
|
8 |
|
* @param ActionSchema $action the action node from composer.json |
|
|
|
|
288
|
8 |
|
*/ |
289
|
8 |
|
private function generateAction($actionName) { |
290
|
7 |
|
$this->logger->info('Generate Action: ' . $actionName); |
291
|
7 |
|
$input = $this->io->getInput(); |
292
|
7 |
|
|
293
|
7 |
|
// get action and create it if it doesn't exist |
294
|
8 |
|
$action = $this->getAction($actionName); |
295
|
|
|
|
296
|
|
|
if (($title = $input->getOption('title')) !== null) { |
297
|
7 |
|
$action->setTitle($title); |
298
|
7 |
|
} |
299
|
7 |
|
|
300
|
7 |
|
if (Text::create($action->getTitle())->isEmpty()) { |
301
|
7 |
|
throw new \RuntimeException(sprintf('Cannot create action %s, because I am missing a title for it', $actionName)); |
302
|
2 |
|
} |
303
|
2 |
|
|
304
|
7 |
|
if (($classname = $input->getOption('classname')) !== null) { |
305
|
7 |
|
$action->setClass($classname); |
306
|
1 |
|
} |
307
|
1 |
|
|
308
|
1 |
|
// guess classname if there is none set yet |
309
|
1 |
|
if (Text::create($action->getClass())->isEmpty()) { |
310
|
1 |
|
$action->setClass($this->guessClassname($actionName)); |
311
|
6 |
|
} |
312
|
|
|
|
313
|
7 |
|
// guess title if there is none set yet |
314
|
|
|
if (Text::create($action->getTitle())->isEmpty() |
315
|
7 |
|
&& $this->modelService->isModelAction($action) |
316
|
|
|
&& $this->modelService->isCrudAction($action)) { |
317
|
|
|
$modelName = $this->modelService->getModelNameByAction($action); |
318
|
|
|
$type = $this->modelService->getOperationByAction($action); |
319
|
|
|
$action->setTitle($this->getActionTitle($modelName, $type)); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
// set acl |
323
|
|
|
$action->setAcl($this->getAcl($action)); |
324
|
|
|
|
325
|
|
|
// generate code |
326
|
|
|
$this->generateCode($action); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
private function guessClassname($name) { |
330
|
|
|
$namespace = NamespaceResolver::getNamespace('src/action', $this->package); |
331
|
7 |
|
return $namespace . '\\' . NameUtils::toStudlyCase($name) . 'Action'; |
332
|
7 |
|
} |
333
|
7 |
|
|
334
|
|
|
/** |
335
|
|
|
* |
336
|
7 |
|
* @param string $actionName |
337
|
7 |
|
* @return ActionSchema |
338
|
7 |
|
*/ |
339
|
7 |
|
private function getAction($actionName) { |
340
|
7 |
|
$action = $this->packageService->getAction($actionName); |
341
|
|
|
if ($action == null) { |
342
|
|
|
$action = new ActionSchema($actionName); |
343
|
7 |
|
$module = $this->packageService->getModule(); |
344
|
|
|
$module->addAction($action); |
345
|
1 |
|
} |
346
|
1 |
|
return $action; |
347
|
|
|
} |
348
|
1 |
|
|
349
|
1 |
|
private function getAcl(ActionSchema $action) { |
350
|
1 |
|
$acls = []; |
351
|
|
|
$acl = $this->io->getInput()->getOption('acl'); |
352
|
|
|
if ($acl !== null && count($acl) > 0) { |
353
|
1 |
|
if (!is_array($acl)) { |
354
|
1 |
|
$acl = [$acl]; |
355
|
1 |
|
} |
356
|
|
|
foreach ($acl as $group) { |
357
|
|
|
if (strpos($group, ',') !== false) { |
358
|
|
|
$groups = explode(',', $group); |
359
|
6 |
|
foreach ($groups as $g) { |
360
|
6 |
|
$acls[] = trim($g); |
361
|
6 |
|
} |
362
|
6 |
|
} else { |
363
|
6 |
|
$acls[] = $group; |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
367
|
7 |
|
return $acls; |
368
|
5 |
|
} |
369
|
1 |
|
|
370
|
1 |
|
// read default from package |
371
|
1 |
|
if (!$action->getAcl()->isEmpty()) { |
372
|
5 |
|
return $action->getAcl()->toArray(); |
373
|
5 |
|
} |
374
|
|
|
|
375
|
5 |
|
return $acls; |
376
|
5 |
|
} |
377
|
|
|
|
378
|
5 |
|
/** |
379
|
4 |
|
* Generates code for an action |
380
|
4 |
|
* |
381
|
4 |
|
* @param ActionSchema $action |
382
|
5 |
|
*/ |
383
|
|
|
private function generateCode(ActionSchema $action) { |
384
|
2 |
|
$input = $this->io->getInput(); |
385
|
2 |
|
$trait = null; |
|
|
|
|
386
|
2 |
|
|
387
|
2 |
|
// class |
388
|
2 |
|
$class = new PhpClass($action->getClass()); |
389
|
|
|
$filename = $this->codegenService->getFilename($class); |
390
|
|
|
$traitNs = $class->getNamespace() . '\\base'; |
391
|
7 |
|
$traitName = $class->getName() . 'Trait'; |
392
|
7 |
|
$overwrite = false; |
393
|
|
|
|
394
|
|
|
// load from file, when class exists |
395
|
|
|
if (file_exists($filename)) { |
396
|
|
|
// load trait |
397
|
|
|
$trait = new PhpTrait($traitNs . '\\' . $traitName); |
398
|
|
|
$traitFile = new File($this->codegenService->getFilename($trait)); |
399
|
|
|
|
400
|
|
|
if ($traitFile->exists()) { |
401
|
|
|
$trait = PhpTrait::fromFile($traitFile->getPathname()); |
|
|
|
|
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
// load class |
405
|
|
|
$class = PhpClass::fromFile($filename); |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
// anyway seed class information |
409
|
|
|
else { |
410
|
|
|
$overwrite = true; |
411
|
|
|
$class->setParentClassName('AbstractAction'); |
412
|
|
|
$class->setDescription($action->getTitle()); |
413
|
|
|
$class->setLongDescription($action->getDescription()); |
414
|
|
|
$this->codegenService->addAuthors($class, $this->package); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
// create base trait |
418
|
|
|
$modelName = $input->getOption('model'); |
419
|
|
|
if ($modelName !== null) { |
420
|
|
|
$type = $this->packageService->getActionType($action->getName(), $modelName); |
421
|
|
|
$generator = GeneratorFactory::createActionTraitGenerator($type, $this->service); |
422
|
|
|
$trait = $generator->generate($traitNs . '\\' . $traitName, $action); |
423
|
|
|
|
424
|
|
|
$this->codegenService->addAuthors($trait, $this->package); |
425
|
|
|
$this->codegenService->dumpStruct($trait, true); |
426
|
|
|
|
427
|
|
|
if (!$class->hasTrait($trait)) { |
428
|
|
|
$class->addTrait($trait); |
429
|
|
|
$overwrite = true; |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
// create class generator |
434
|
|
|
if ($modelName === null && !$class->hasMethod('run')) { |
435
|
|
|
$overwrite = true; |
436
|
|
|
$generator = new BlankActionGenerator($this->service); |
437
|
|
|
} else { |
438
|
|
|
$generator = new NoopActionGenerator($this->service); |
439
|
|
|
} |
440
|
|
|
|
441
|
|
|
$class = $generator->generate($class); |
442
|
|
|
$overwrite = $overwrite || $input->getOption('force'); |
443
|
|
|
|
444
|
|
|
$this->codegenService->dumpStruct($class, $overwrite); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
private function prepareModelForApi(Table $model) { |
448
|
|
|
$class = new PhpClass(str_replace('\\\\', '\\', $model->getNamespace() . '\\' . $model->getPhpName())); |
449
|
|
|
$file = new File($this->codegenService->getFilename($class)); |
450
|
|
|
|
451
|
|
|
if ($file->exists()) { |
452
|
|
|
// generate serializer |
453
|
|
|
$generator = new SerializerGenerator($this->service); |
454
|
|
|
$serializer = $generator->generate($model); |
455
|
|
|
$this->codegenService->dumpStruct($serializer, true); |
456
|
|
|
|
457
|
|
|
// add serializer + APIModelInterface |
458
|
|
|
$class = PhpClass::fromFile($this->codegenService->getFilename($class)); |
459
|
|
|
if (!$class->hasInterface('ApiModelInterface')) { |
460
|
|
|
$class |
461
|
|
|
->addUseStatement($serializer->getQualifiedName()) |
462
|
|
|
->addUseStatement('keeko\\framework\\model\\ApiModelInterface') |
463
|
|
|
->addInterface('ApiModelInterface') |
464
|
|
|
->setProperty(PhpProperty::create('serializer') |
465
|
|
|
->setStatic(true) |
466
|
|
|
->setVisibility('private') |
467
|
|
|
) |
468
|
|
|
->setMethod(PhpMethod::create('getSerializer') |
469
|
|
|
->setStatic(true) |
470
|
|
|
->setBody($this->twig->render('get-serializer.twig', [ |
471
|
|
|
'class' => $class->getName() |
472
|
|
|
])) |
473
|
|
|
) |
474
|
|
|
; |
475
|
|
|
|
476
|
|
|
$this->codegenService->dumpStruct($class, true); |
477
|
|
|
} |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
private function generateToOneRelationshipAction(Table $model, Table $foreign, ForeignKey $fk) { |
482
|
|
|
$module = $this->package->getKeeko()->getModule(); |
483
|
|
|
$fkModelName = $foreign->getPhpName(); |
484
|
|
|
$actionNamePrefix = sprintf('%s-to-%s-relationship', $model->getOriginCommonName(), $foreign->getOriginCommonName()); |
485
|
|
|
|
486
|
|
|
$generators = [ |
487
|
|
|
'read' => new ToOneRelationshipReadActionGenerator($this->service), |
488
|
|
|
'update' => new ToOneRelationshipUpdateActionGenerator($this->service) |
489
|
|
|
]; |
490
|
|
|
$titles = [ |
491
|
|
|
'read' => 'Reads the relationship of {model} to {foreign}', |
492
|
|
|
'update' => 'Updates the relationship of {model} to {foreign}' |
493
|
|
|
]; |
494
|
|
|
|
495
|
|
|
foreach (array_keys($generators) as $type) { |
496
|
|
|
// generate fqcn |
497
|
|
|
$className = sprintf('%s%s%sAction', $model->getPhpName(), $fkModelName, ucfirst($type)); |
498
|
|
|
$fqcn = $this->modelService->getRootNamespace() . '\\action\\' . $className; |
499
|
|
|
|
500
|
|
|
// generate action |
501
|
|
|
$action = new ActionSchema($actionNamePrefix . '-' . $type); |
502
|
|
|
$action->addAcl('admin'); |
503
|
|
|
$action->setClass($fqcn); |
504
|
|
|
$action->setTitle(str_replace( |
505
|
|
|
['{model}', '{foreign}'], |
506
|
|
|
[$model->getOriginCommonName(), $foreign->getoriginCommonName()], |
507
|
|
|
$titles[$type]) |
508
|
|
|
); |
509
|
|
|
$module->addAction($action); |
510
|
|
|
|
511
|
|
|
// generate class |
512
|
|
|
$generator = $generators[$type]; |
513
|
|
|
$class = $generator->generate(new PhpClass($fqcn), $model, $foreign, $fk); |
514
|
|
|
$this->codegenService->dumpStruct($class, true); |
515
|
|
|
} |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
private function generateToManyRelationshipAction(Table $model, Table $foreign, Table $middle) { |
519
|
|
|
$module = $this->package->getKeeko()->getModule(); |
520
|
|
|
$fkModelName = $foreign->getPhpName(); |
521
|
|
|
$actionNamePrefix = sprintf('%s-to-%s-relationship', $model->getOriginCommonName(), $foreign->getOriginCommonName()); |
522
|
|
|
|
523
|
|
|
$generators = [ |
524
|
|
|
'read' => new ToManyRelationshipReadActionGenerator($this->service), |
525
|
|
|
'update' => new ToManyRelationshipUpdateActionGenerator($this->service), |
526
|
|
|
'add' => new ToManyRelationshipAddActionGenerator($this->service), |
527
|
|
|
'remove' => new ToManyRelationshipRemoveActionGenerator($this->service) |
528
|
|
|
]; |
529
|
|
|
$titles = [ |
530
|
|
|
'read' => 'Reads the relationship of {model} to {foreign}', |
531
|
|
|
'update' => 'Updates the relationship of {model} to {foreign}', |
532
|
|
|
'add' => 'Adds {foreign} as relationship to {model}', |
533
|
|
|
'remove' => 'Removes {foreign} as relationship of {model}' |
534
|
|
|
]; |
535
|
|
|
|
536
|
|
|
foreach (array_keys($generators) as $type) { |
537
|
|
|
// generate fqcn |
538
|
|
|
$className = sprintf('%s%s%sAction', $model->getPhpName(), $fkModelName, ucfirst($type)); |
539
|
|
|
$fqcn = $this->modelService->getRootNamespace() . '\\action\\' . $className; |
540
|
|
|
|
541
|
|
|
// generate action |
542
|
|
|
$action = new ActionSchema($actionNamePrefix . '-' . $type); |
543
|
|
|
$action->addAcl('admin'); |
544
|
|
|
$action->setClass($fqcn); |
545
|
|
|
$action->setTitle(str_replace( |
546
|
|
|
['{model}', '{foreign}'], |
547
|
|
|
[$model->getOriginCommonName(), $foreign->getoriginCommonName()], |
548
|
|
|
$titles[$type]) |
549
|
|
|
); |
550
|
|
|
$module->addAction($action); |
551
|
|
|
|
552
|
|
|
// generate class |
553
|
|
|
$generator = $generators[$type]; |
554
|
|
|
$class = $generator->generate(new PhpClass($fqcn), $model, $foreign, $middle); |
555
|
|
|
$this->codegenService->dumpStruct($class, true); |
556
|
|
|
} |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
} |
560
|
|
|
|
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.