Completed
Push — master ( f1c264...f58245 )
by Thomas
04:22
created

GenerateSerializerCommand::generateModel()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 38
rs 8.8571
cc 2
eloc 25
nc 2
nop 1
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 keeko\tools\generator\serializer\base\ModelSerializerTraitGenerator;
8
use keeko\tools\generator\serializer\SkeletonSerializerGenerator;
9
use keeko\tools\helpers\QuestionHelperTrait;
10
use phootwork\file\File;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Console\Question\ConfirmationQuestion;
16
use Symfony\Component\Console\Question\Question;
17
use keeko\tools\utils\NamespaceResolver;
18
use phootwork\lang\Text;
19
use keeko\tools\generator\serializer\ModelSerializerGenerator;
20
21
class GenerateSerializerCommand extends AbstractGenerateCommand {
22
23
	use QuestionHelperTrait;
24
	
25
	private $twig;
26
27
	protected function configure() {
28
		$this
29
			->setName('generate:serializer')
30
			->setDescription('Generates a serializer')
31
			->addArgument(
32
				'name',
33
				InputArgument::OPTIONAL,
34
				'The name of the action for which the serializer should be generated.'
35
			)
36
			->addOption(
37
				'model',
38
				'm',
39
				InputOption::VALUE_OPTIONAL,
40
				'The model for which the serializer should be generated, when there is no name argument (if ommited all models will be generated)'
41
			)
42
		;
43
		
44
		$this->configureGenerateOptions();
45
		
46
		parent::configure();
47
	}
48
49
	protected function initialize(InputInterface $input, OutputInterface $output) {
50
		parent::initialize($input, $output);
51
52
		$loader = new \Twig_Loader_Filesystem($this->service->getConfig()->getTemplateRoot() . '/serializer');
53
		$this->twig = new \Twig_Environment($loader);
54
	}
55
56
	/**
57
	 * Checks whether actions can be generated at all by reading composer.json and verify
58
	 * all required information are available
59
	 */
60
	private function preCheck() {
61
		$module = $this->packageService->getModule();
62
		if ($module === null) {
63
			throw new \DomainException('No module definition found in composer.json - please run `keeko init`.');
64
		}
65
	}
66
	
67
	protected function interact(InputInterface $input, OutputInterface $output) {
68
		$this->preCheck();
69
		
70
		// check if the dialog can be skipped
71
		$name = $input->getArgument('name');
72
		$model = $input->getOption('model');
73
		
74
		if ($model !== null) {
75
			return;
76
		} else if ($name !== null) {
77
			$generateModel = false;
78
		} else {
79
			$modelQuestion = new ConfirmationQuestion('Do you want to generate a serializer based off a model?');
80
			$generateModel = $this->askConfirmation($modelQuestion);
81
		}
82
		
83
		// ask questions for a model
84
		if ($generateModel) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $generateModel of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
85
			$schema = str_replace(getcwd(), '', $this->modelService->getSchema());
86
			$allQuestion = new ConfirmationQuestion(sprintf('For all models in the schema (%s)?', $schema));
87
			$allModels = $this->askConfirmation($allQuestion);
88
89
			if (!$allModels) {
90
				$modelQuestion = new Question('Which model');
91
				$modelQuestion->setAutocompleterValues($this->modelService->getModelNames());
92
				$model = $this->askQuestion($modelQuestion);
93
				$input->setOption('model', $model);
94
			}
95
		} 
96
		
97
		// ask question for a skeleton
98
		else if (empty($name)) {
99
			// ask for classname
100
			$name = $this->askQuestion(new Question('Classname', $name));
101
			$input->setArgument('name', $name);
102
		}
103
	}
104
105
	protected function execute(InputInterface $input, OutputInterface $output) {
106
		$this->preCheck();
107
		
108
		$name = $input->getArgument('name');
109
		$model = $input->getOption('model');
110
111
		// only a specific action
112
		if ($name) {
113
			$this->generateSkeleton($name);
114
		}
115
116
		// create action(s) from a model
117
		else if ($model) {
118
			$this->generateModel($model);
119
		}
120
121
		// anyway, generate all models
122
		else {
123
			foreach ($this->modelService->getModels() as $model) {
124
				$modelName = $model->getOriginCommonName();
125
				$input->setOption('model', $modelName);
126
				$this->generateModel($modelName);
127
			}
128
		}
129
	}
130
131
	private function generateModel($modelName) {
132
		$this->logger->info('Generate Serializer from Model: ' . $modelName);
133
		$model = $this->modelService->getModel($modelName);
134
		
135
		// generate class
136
		$generator = new ModelSerializerGenerator($this->service);
137
		$serializer = $generator->generate($model);
138
		$this->codegenService->dumpStruct($serializer, true);
139
		
140
		// generate trait
141
		$generator = new ModelSerializerTraitGenerator($this->service);
142
		$trait = $generator->generate($model);
143
		$this->codegenService->dumpStruct($trait, true);
144
		
145
		// add serializer + ApiModelInterface on the model
146
		$class = new PhpClass(str_replace('\\\\', '\\', $model->getNamespace() . '\\' . $model->getPhpName()));
147
		$file = $this->codegenService->getFile($class);
148
		if ($file->exists()) {
149
			$class = PhpClass::fromFile($this->codegenService->getFilename($class));
150
			$class
151
				->addUseStatement($serializer->getQualifiedName())
152
				->addUseStatement('keeko\\framework\\model\\ApiModelInterface')
153
				->addInterface('ApiModelInterface')
154
				->setProperty(PhpProperty::create('serializer')
155
					->setStatic(true)
156
					->setVisibility('private')
157
				)
158
				->setMethod(PhpMethod::create('getSerializer')
159
					->setStatic(true)
160
					->setBody($this->twig->render('get-serializer.twig', [
161
						'class' => $serializer->getName()
162
					]))
163
				)
164
			;
165
		
166
			$this->codegenService->dumpStruct($class, true);
167
		}
168
	}
169
	
170
	/**
171
	 * Generates a skeleton serializer
172
	 *  
173
	 * @param string $name
174
	 */
175
	private function generateSkeleton($name) {
176
		$this->logger->info('Generate Skeleton Serializer: ' . $name);
177
		$input = $this->io->getInput();
178
179
		$className = NamespaceResolver::getNamespace('src/serializer', $this->package) . '\\' . $name;
180
		
181
		if (!Text::create($className)->endsWith('Serializer')) {
182
			$className .= 'Serializer';
183
		}
184
185
		// generate code
186
		$generator = new SkeletonSerializerGenerator($this->service);
187
		$class = $generator->generate($className);
188
		$this->codegenService->dumpStruct($class, $input->getOption('force'));
189
	}
190
	
191
	
192
193
}
194