Completed
Push — master ( c7b947...966759 )
by Thomas
08:09
created

ModelSerializerTraitGenerator   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 17
c 8
b 0
f 1
lcom 1
cbo 15
dl 0
loc 207
rs 9.1666

5 Methods

Rating   Name   Duplication   Size   Complexity  
A generateIdentifyingMethods() 0 18 1
A generate() 0 12 1
B generateAttributeMethods() 0 41 4
B generateHydrateMethod() 0 44 5
B generateRelationshipMethods() 0 80 6
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 Propel\Generator\Model\Table;
10
use keeko\tools\model\ManyRelationship;
11
use keeko\tools\model\Relationship;
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
		$class = new PhpTrait($fqcn);
24
		
25
		$this->generateIdentifyingMethods($class, $model);
26
		$this->generateAttributeMethods($class, $model);
27
		$this->generateHydrateMethod($class, $model);
28
// 		$this->generateRelationshipMethods($class, $model);
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% 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...
29
		
30
		return $class;
31
	}
32
	
33
	protected function generateIdentifyingMethods(PhpTrait $class, Table $model) {
34
		$package = $this->packageService->getPackage();
35
		$type = sprintf('%s/%s', $package->getCleanName(), NameUtils::dasherize($model->getOriginCommonName()));
36
		
37
		$class->setMethod(PhpMethod::create('getId')
38
			->addParameter(PhpParameter::create('model'))
39
			->setBody($this->twig->render('getId.twig'))
40
			->setType('string')
41
		);
42
		
43
		$class->setMethod(PhpMethod::create('getType')
44
			->addParameter(PhpParameter::create('model'))
45
			->setBody($this->twig->render('getType.twig', [
46
				'type' => $type
47
			]))
48
			->setType('string')
49
		);
50
	}
51
	
52
	protected function generateAttributeMethods(PhpTrait $class, Table $model) {
53
		$readFields = $this->codegenService->getReadFields($model->getOriginCommonName());
54
		$attrs = '';
55
		
56
		foreach ($readFields as $field) {
57
			$col = $model->getColumn($field);
58
			$param = $col->isTemporalType() ? '\DateTime::ISO8601' : '';
59
			$attrs .= sprintf("\t'%s' => \$model->get%s(%s),\n", 
60
				NameUtils::dasherize($field), $col->getPhpName(), $param
61
			);
62
		}
63
		
64
		if (count($field) > 0) {
0 ignored issues
show
Bug introduced by
The variable $field seems to be defined by a foreach iteration on line 56. 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...
65
			$attrs = substr($attrs, 0, -1);
66
		}
67
		
68
		$class->setMethod(PhpMethod::create('getAttributes')
69
			->addParameter(PhpParameter::create('model'))
70
			->addParameter(PhpParameter::create('fields')->setType('array')->setDefaultValue(null))
71
			->setBody($this->twig->render('getAttributes.twig', [
72
				'attrs' => $attrs
73
			]))
74
		);
75
		
76
		$class->setMethod(PhpMethod::create('getSortFields')
77
			->setBody($this->twig->render('getFields.twig', [
78
				'fields' => $this->codegenService->arrayToCode(array_map(function ($field) {
79
					return NameUtils::dasherize($field);
80
				}, $readFields))
81
			]))
82
		);
83
		
84
		$readFields = $this->codegenService->getReadFields($model->getOriginCommonName());
85
		$class->setMethod(PhpMethod::create('getFields')
86
			->setBody($this->twig->render('getFields.twig', [
87
				'fields' => $this->codegenService->arrayToCode(array_map(function ($field) {
88
					return NameUtils::dasherize($field);
89
				}, $readFields))
90
			]))
91
		);
92
	}
93
	
94
	protected function generateHydrateMethod(PhpTrait $trait, Table $model) {
95
		if ($model->isReadOnly()) {
96
			$body = $this->twig->render('hydrate-readonly.twig');
97
		} else {
98
			$trait->addUseStatement('keeko\\framework\\utils\\HydrateUtils');
99
			$modelName = $model->getOriginCommonName();
100
			$conversions = $this->codegenService->getCodegen()->getWriteConversion($modelName);
101
			$fields = $this->codegenService->getWriteFields($modelName);
102
			$code = '';
103
			
104
			foreach ($fields as $field) {
105
				$code .= sprintf("'%s'", NameUtils::dasherize($field));
106
				if (isset($conversions[$field])) {
107
					$code .= ' => function($v) {' . "\n\t" . 'return ' . $conversions[$field] . ';' . "\n" . '}';
108
				}
109
		
110
				$code .= ', ';
111
			}
112
			
113
			if (strlen($code) > 0) {
114
				$code = substr($code, 0, -2);
115
			}
116
			
117
			$code = sprintf('[%s]', $code);
118
			$body = $this->twig->render('hydrate.twig', [
119
				'code' => $code
120
			]);
121
			
122
			$trait->setMethod(PhpMethod::create('hydrateRelationships')
123
				->addParameter(PhpParameter::create('model'))
124
				->addParameter(PhpParameter::create('data'))
125
				->setAbstract(true)
126
				->setType('void')
127
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
128
			);
129
		}
130
		
131
		$trait->setMethod(PhpMethod::create('hydrate')
132
			->addParameter(PhpParameter::create('model'))
133
			->addParameter(PhpParameter::create('data'))
134
			->setBody($body)
135
			->setType('mixed', 'The model')
136
		);
137
	}
138
	
139
	protected function generateRelationshipMethods(PhpTrait $class, Table $model) {
140
		if ($model->isReadOnly()) {
141
			return;
142
		}
143
144
		$fields = [];
145
		$relationships = $this->modelService->getRelationships($model);
146
		
147
		if ($relationships->size() > 0) {
148
			$class->addUseStatement('Tobscure\\JsonApi\\Relationship');
149
			$class->setMethod(PhpMethod::create('addRelationshipSelfLink')
150
				->addParameter(PhpParameter::create('relationship')
151
					->setType('Relationship')
152
				)
153
				->addParameter(PhpParameter::create('model')
154
					->setType('mixed')
155
				)
156
				->addParameter(PhpParameter::create('related')
157
					->setType('string')
158
				)
159
				->setAbstract(true)
160
				->setVisibility(PhpMethod::VISIBILITY_PROTECTED)
161
				->setType('Relationship')
162
			);
163
		}
164
		
165
		foreach ($relationships->getAll() as $rel) {
166
			
167
			// to-many
168
			if ($rel instanceof ManyRelationship) {
0 ignored issues
show
Bug introduced by
The class keeko\tools\model\ManyRelationship does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
169
				$foreign = $rel->getForeign();
170
				$relatedName = $rel->getRelatedName();
171
				
172
				$typeName = $rel->getRelatedTypeName();
173
				$method = NameUtils::toCamelCase($typeName);
174
				$fields[$typeName] = $foreign->getPhpName() . '::getSerializer()->getType(null)';
175
				$class->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName());
176
				$class->addUseStatement('Tobscure\\JsonApi\\Collection');
177
				
178
				// read
179
				$body = $this->twig->render('to-many-read.twig', [
180
					'getter' => NameUtils::pluralize($relatedName),
181
					'class' => $relatedName,
182
					'related' => $typeName
183
				]);
184
			}
185
			
186
			// to-one
187
			else if ($rel instanceof Relationship) {
188
				$foreign = $rel->getForeign();
189
				$relatedName = $rel->getRelatedName();
190
				
191
				$typeName = $rel->getRelatedTypeName();
192
				$method = NameUtils::toCamelCase($typeName);
193
				$fields[$typeName] = $foreign->getPhpName() . '::getSerializer()->getType(null)';
194
				$class->addUseStatement($foreign->getNamespace() . '\\' . $foreign->getPhpName());
195
				$class->addUseStatement('Tobscure\\JsonApi\\Resource');
196
				
197
				// read
198
				$body = $this->twig->render('to-one-read.twig', [
199
					'ref' => $relatedName,
200
					'class' => $foreign->getPhpName(),
201
					'related' => $typeName
202
				]);
203
			}
204
			
205
			// needs to go down
206
			$class->setMethod(PhpMethod::create($method)
0 ignored issues
show
Bug introduced by
The variable $method does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
207
				->addParameter(PhpParameter::create('model'))
208
				->setBody($body)
0 ignored issues
show
Bug introduced by
The variable $body does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
209
				->setType('Relationship')
210
			);
211
		}
212
		
213
		$class->setMethod(PhpMethod::create('getRelationships')
214
			->setBody($this->twig->render('getRelationships.twig', [
215
				'fields' => $fields
216
			]))
217
		);
218
	}
219
}