Completed
Push — master ( c5048d...43a701 )
by Thomas
06:21
created

generateRelationshipMethods()   D

Complexity

Conditions 9
Paths 15

Size

Total Lines 140
Code Lines 90

Duplication

Lines 0
Ratio 0 %

Importance

Changes 8
Bugs 2 Features 1
Metric Value
c 8
b 2
f 1
dl 0
loc 140
rs 4.8196
cc 9
eloc 90
nc 15
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace keeko\tools\generator\serializer\base;
3
4
use gossi\codegen\model\PhpMethod;
5
use gossi\codegen\model\PhpParameter;
6
use gossi\codegen\model\PhpTrait;
7
use keeko\framework\utils\NameUtils;
8
use keeko\tools\generator\serializer\AbstractSerializerGenerator;
9
use keeko\tools\model\Relationship;
10
use Propel\Generator\Model\Table;
11
use gossi\codegen\model\PhpProperty;
12
13
class ModelSerializerTraitGenerator extends AbstractSerializerGenerator {
14
15
	/**
16
	 *
17
	 * @param Table $model
18
	 * @return PhpTrait
19
	 */
20
	public function generate(Table $model) {
21
		$ns = $this->packageService->getNamespace();
22
		$fqcn = sprintf('%s\\serializer\\base\\%sSerializerTrait', $ns, $model->getPhpName());
23
		$trait = new PhpTrait($fqcn);
24
25
		$this->generateIdentifyingMethods($trait, $model);
26
		$this->generateAttributeMethods($trait, $model);
27
		$this->generateHydrateMethod($trait, $model);
28
		$this->generateRelationshipMethods($trait, $model);
29
		$this->generateTypeInferencerAccess($trait);
30
31
		return $trait;
32
	}
33
34
	protected function generateIdentifyingMethods(PhpTrait $trait, Table $model) {
35
		$package = $this->packageService->getPackage();
36
		$type = sprintf('%s/%s', $package->getKeeko()->getModule()->getSlug(), NameUtils::dasherize($model->getOriginCommonName()));
37
38
		$trait->setMethod(PhpMethod::create('getId')
39
			->addParameter(PhpParameter::create('model'))
40
			->setBody($this->twig->render('getId.twig'))
41
			->setType('string')
42
		);
43
44
		$trait->setMethod(PhpMethod::create('getType')
45
			->addParameter(PhpParameter::create('model'))
46
			->setBody($this->twig->render('getType.twig', [
47
				'type' => $type
48
			]))
49
			->setType('string')
50
		);
51
	}
52
53
	protected function generateAttributeMethods(PhpTrait $trait, Table $model) {
54
		$readFields = $this->codegenService->getReadFields($model->getOriginCommonName());
55
		$attrs = '';
56
57
		foreach ($readFields as $field) {
58
			$col = $model->getColumn($field);
59
			$param = $col->isTemporalType() ? '\DateTime::ISO8601' : '';
60
			$attrs .= sprintf("\t'%s' => \$model->get%s(%s),\n",
61
				NameUtils::dasherize($field), $col->getPhpName(), $param
62
			);
63
		}
64
65
		if (count($field) > 0) {
0 ignored issues
show
Bug introduced by
The variable $field seems to be defined by a foreach iteration on line 57. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
66
			$attrs = substr($attrs, 0, -2);
67
		}
68
69
		$trait->setMethod(PhpMethod::create('getAttributes')
70
			->addParameter(PhpParameter::create('model'))
71
			->addParameter(PhpParameter::create('fields')->setType('array')->setDefaultValue(null))
0 ignored issues
show
Deprecated Code introduced by
The method gossi\codegen\model\part...Part::setDefaultValue() has been deprecated with message: use `setValue()` instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
72
			->setBody($this->twig->render('getAttributes.twig', [
73
				'attrs' => $attrs
74
			]))
75
		);
76
77
		$trait->setMethod(PhpMethod::create('getSortFields')
78
			->setBody($this->twig->render('getFields.twig', [
79
				'fields' => $this->codegenService->arrayToCode(array_map(function ($field) {
80
					return NameUtils::dasherize($field);
81
				}, $readFields))
82
			]))
83
		);
84
85
		$readFields = $this->codegenService->getReadFields($model->getOriginCommonName());
86
		$trait->setMethod(PhpMethod::create('getFields')
87
			->setBody($this->twig->render('getFields.twig', [
88
				'fields' => $this->codegenService->arrayToCode(array_map(function ($field) {
89
					return NameUtils::dasherize($field);
90
				}, $readFields))
91
			]))
92
		);
93
	}
94
95
	protected function generateHydrateMethod(PhpTrait $trait, Table $model) {
96
		if ($model->isReadOnly()) {
97
			$body = $this->twig->render('hydrate-readonly.twig');
98
		} else {
99
			$trait->addUseStatement('keeko\\framework\\utils\\HydrateUtils');
100
			$modelName = $model->getOriginCommonName();
101
			$normalizer = $this->codegenService->getCodegen()->getNormalizer($modelName);
102
			$fields = $this->codegenService->getWriteFields($modelName);
103
			$code = '';
104
105
			foreach ($fields as $field) {
106
				$code .= sprintf("'%s'", NameUtils::dasherize($field));
107
				if ($normalizer->has($field)) {
108
					$code .= $this->twig->render('normalizer.twig', [
109
						'class' => $normalizer->get($field)
110
					]);
111
				}
112
113
				$code .= ', ';
114
			}
115
116
			if (strlen($code) > 0) {
117
				$code = substr($code, 0, -2);
118
			}
119
120
			$code = sprintf('[%s]', $code);
121
			$body = $this->twig->render('hydrate.twig', [
122
				'code' => $code,
123
				'normalizer' => $normalizer->size() > 0
124
			]);
125
126
			$trait->setMethod(PhpMethod::create('hydrateRelationships')
127
				->addParameter(PhpParameter::create('model'))
128
				->addParameter(PhpParameter::create('data'))
129
				->setAbstract(true)
130
				->setType('void')
131
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
132
			);
133
		}
134
135
		$trait->setMethod(PhpMethod::create('hydrate')
136
			->addParameter(PhpParameter::create('model'))
137
			->addParameter(PhpParameter::create('data'))
138
			->setBody($body)
139
			->setType('mixed', 'The model')
140
		);
141
	}
142
143
	protected function generateRelationshipMethods(PhpTrait $trait, Table $model) {
144
		if ($model->isReadOnly()) {
145
			return;
146
		}
147
148
		$fields = [];
149
		$methods = [];
150
		$plural = [];
151
		$relationships = $this->modelService->getRelationships($model);
152
153
		if ($relationships->size() > 0) {
154
			$trait->addUseStatement('Tobscure\\JsonApi\\Relationship');
155
			$trait->setMethod(PhpMethod::create('addRelationshipSelfLink')
156
				->addParameter(PhpParameter::create('relationship')
157
					->setType('Relationship')
158
				)
159
				->addParameter(PhpParameter::create('model')
160
					->setType('mixed')
161
				)
162
				->addParameter(PhpParameter::create('related')
163
					->setType('string')
164
				)
165
				->setAbstract(true)
166
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
167
				->setType('Relationship')
168
			);
169
		}
170
171
		foreach ($relationships->getAll() as $rel) {
172
			// one-to-one
173
			if ($rel->getType() == Relationship::ONE_TO_ONE) {
174
				$foreign = $rel->getForeign();
175
				$relatedName = $rel->getRelatedName();
176
				$typeName = $rel->getRelatedTypeName();
177
				$method = NameUtils::toCamelCase($relatedName);
178
				$fields[$typeName] = $foreign->getPhpName() . '::getSerializer()->getType(null)';
179
				$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName());
180
				$trait->addUseStatement('Tobscure\\JsonApi\\Resource');
181
182
				// read
183
				$body = $this->twig->render('to-one-read.twig', [
184
					'class' => $foreign->getPhpName(),
185
					'related' => $relatedName,
186
					'related_type' => $typeName
187
				]);
188
			}
189
190
			// ?-to-many
191
			else {
192
				$foreign = $rel->getForeign();
193
				$typeName = $rel->getRelatedPluralTypeName();
194
				$method = NameUtils::toCamelCase($rel->getRelatedPluralName());
195
				$fields[$typeName] = $foreign->getPhpName() . '::getSerializer()->getType(null)';
196
				$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName());
197
				$trait->addUseStatement('Tobscure\\JsonApi\\Collection');
198
199
				// read
200
				$body = $this->twig->render('to-many-read.twig', [
201
					'class' => $foreign->getPhpName(),
202
					'related' => $typeName,
203
					'related_type' => $rel->getRelatedTypeName()
204
				]);
205
206
				// method name for collection
207
				$methods[$typeName] = NameUtils::toStudlyCase(NameUtils::singularize($typeName));
208
				$plural[$typeName] = NameUtils::pluralize(NameUtils::toStudlyCase(NameUtils::singularize($typeName)));
209
				if ($rel->getType() == Relationship::MANY_TO_MANY
210
						&& $rel->getForeign() == $rel->getModel()) {
211
					$lk = $rel->getLocalKey();
212
					$methods[$typeName] = $foreign->getPhpName() . 'RelatedBy' . $lk->getLocalColumn()->getPhpName();
213
					$plural[$typeName] = NameUtils::pluralize($foreign->getPhpName()) . 'RelatedBy' . $lk->getLocalColumn()->getPhpName();
214
				}
215
			}
216
217
			// set read method on class
218
			$trait->setMethod(PhpMethod::create($method)
219
				->addParameter(PhpParameter::create('model'))
220
				->setBody($body)
221
				->setType('Relationship')
222
			);
223
224
			// add reverse many-to-many
225
			if ($rel->getType() == Relationship::MANY_TO_MANY
226
					&& $rel->getForeign() == $rel->getModel()) {
227
				$foreign = $rel->getForeign();
228
				$typeName = $rel->getReverseRelatedPluralTypeName();
229
				$method = NameUtils::toCamelCase($rel->getReverseRelatedPluralName());
230
				$fields[$typeName] = $foreign->getPhpName() . '::getSerializer()->getType(null)';
231
				$trait->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName());
232
				$trait->addUseStatement('Tobscure\\JsonApi\\Collection');
233
234
				// read
235
				$body = $this->twig->render('to-many-read.twig', [
236
					'class' => $foreign->getPhpName(),
237
					'related' => $typeName,
238
					'related_type' => $rel->getReverseRelatedTypeName()
239
				]);
240
241
				$fk = $rel->getForeignKey();
242
				$methods[$typeName] = $foreign->getPhpName() . 'RelatedBy' . $fk->getLocalColumn()->getPhpName();
243
				$plural[$typeName] = NameUtils::pluralize($foreign->getPhpName()) . 'RelatedBy' . $fk->getLocalColumn()->getPhpName();
244
245
				// set read method on class
246
				$trait->setMethod(PhpMethod::create($method)
247
					->addParameter(PhpParameter::create('model'))
248
					->setBody($body)
249
					->setType('Relationship')
250
				);
251
			}
252
		}
253
254
		// method: getRelationships() : array
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
255
		$trait->setMethod(PhpMethod::create('getRelationships')
256
			->setBody($this->twig->render('getRelationships.twig', [
257
				'fields' => $fields
258
			]))
259
		);
260
261
		// method: getCollectionMethodName($relatedName) : string
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
262
		$trait->setProperty(PhpProperty::create('methodNames')
263
			->setExpression($this->codegenService->mapToCode($methods))
264
			->setVisibility(PhpProperty::VISIBILITY_PRIVATE)
265
		);
266
		$trait->setMethod(PhpMethod::create('getCollectionMethodName')
267
			->addParameter(PhpParameter::create('relatedName'))
268
			->setBody($this->twig->render('getCollectionMethodName.twig'))
269
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
270
		);
271
272
		// method: getCollectionMethodPluralName($relatedName) : string
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
273
		$trait->setProperty(PhpProperty::create('methodPluralNames')
274
			->setExpression($this->codegenService->mapToCode($plural))
275
			->setVisibility(PhpProperty::VISIBILITY_PRIVATE)
276
		);
277
		$trait->setMethod(PhpMethod::create('getCollectionMethodPluralName')
278
			->addParameter(PhpParameter::create('relatedName'))
279
			->setBody($this->twig->render('getCollectionMethodPluralName.twig'))
280
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
281
		);
282
	}
283
284
	protected function generateTypeInferencerAccess(PhpTrait $trait) {
285
		$namespace = $this->factory->getNamespaceGenerator()->getSerializerNamespace();
286
		$trait->addUseStatement($namespace . '\\TypeInferencer');
287
		$trait->setMethod(PhpMethod::create('getTypeInferencer')
288
			->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
289
			->setBody($this->twig->render('getTypeInferencer.twig'))
290
		);
291
	}
292
}