Passed
Push — master ( f32e65...4e94c9 )
by Michael
02:27
created

AttributeHandler   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 255
Duplicated Lines 32.55 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 93.33%

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 6
dl 83
loc 255
c 0
b 0
f 0
ccs 84
cts 90
cp 0.9333
rs 8.2608

10 Methods

Rating   Name   Duplication   Size   Complexity  
A registerDataTypeHandler() 0 7 2
A toResource() 0 9 2
B processAttributeToResource() 24 24 5
B processIterableValueToResource() 0 18 6
A processTypeToResource() 16 16 3
A processResourceToType() 16 16 3
A processGenericType() 0 16 4
A fromResource() 0 13 3
B processResourceToAttribute() 27 27 6
B processIterableValueFromResource() 0 22 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like AttributeHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AttributeHandler, and based on these observations, apply Extract Interface, too.

1
<?php
2
declare(strict_types = 1);
3
4
namespace Mikemirten\Component\JsonApi\Mapper\Handler;
5
6
use Mikemirten\Component\JsonApi\Document\ResourceObject;
7
use Mikemirten\Component\JsonApi\Mapper\Definition\Attribute;
8
use Mikemirten\Component\JsonApi\Mapper\Handler\DataTypeHandler\DataTypeHandlerInterface;
9
use Mikemirten\Component\JsonApi\Mapper\Handler\Exception\NotIterableAttribute;
10
use Mikemirten\Component\JsonApi\Mapper\MappingContext;
11
12
/**
13
 * Attribute handler
14
 *
15
 * @package Mikemirten\Component\JsonApi\Mapper\Handler
16
 */
17
class AttributeHandler implements HandlerInterface
18
{
19
    /**
20
     * List of generic types
21
     *
22
     * @var array
23
     */
24
    protected $genericTypes = ['integer', 'float', 'string', 'boolean'];
25
26
    /**
27
     * Data-type handlers
28
     *
29
     * @var DataTypeHandlerInterface[]
30
     */
31
    protected $registeredTypes = [];
32
33
    /**
34
     * Register data-type handler
35
     *
36
     * @param DataTypeHandlerInterface $handler
37
     */
38 2
    public function registerDataTypeHandler(DataTypeHandlerInterface $handler)
39
    {
40 2
        foreach ($handler->supports() as $name)
41
        {
42 2
            $this->registeredTypes[$name] = $handler;
43
        }
44 2
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 9
    public function toResource($object, ResourceObject $resource, MappingContext $context)
50
    {
51 9
        $definitions = $context->getDefinition()->getAttributes();
52
53 9
        foreach ($definitions as $definition)
54
        {
55 9
            $this->processAttributeToResource($object, $resource, $definition);
56
        }
57 7
    }
58
59
    /**
60
     * Process attribute to resource mapping
61
     *
62
     * @param mixed          $object
63
     * @param ResourceObject $resource
64
     * @param Attribute      $definition
65
     */
66 9 View Code Duplication
    protected function processAttributeToResource($object, ResourceObject $resource, Attribute $definition)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
67
    {
68 9
        $name   = $definition->getName();
69 9
        $getter = $definition->getGetter();
70
71 9
        $value = $object->$getter();
72
73 9
        if ($value === null && ! $definition->getProcessNull()) {
74 1
            return;
75
        }
76
77 8
        if ($definition->isMany()) {
78 3
            $value = $this->processIterableValueToResource($value, $definition);
79
80 2
            $resource->setAttribute($name, $value);
81 2
            return;
82
        }
83
        
84 5
        if ($definition->hasType()) {
85 3
            $value = $this->processTypeToResource($definition, $value);
86
        }
87
88 4
        $resource->setAttribute($name, $value);
89 4
    }
90
91
    /**
92
     * Process value declared as many (iterable)
93
     *
94
     * @param mixed      $value
95
     * @param  Attribute $definition
96
     * @return array
97
     */
98 3
    protected function processIterableValueToResource($value, Attribute $definition): array
99
    {
100 3
        if (! $value instanceof \Traversable && ! is_array($value)) {
101 1
            throw new NotIterableAttribute($definition, $value);
102
        }
103
104 2
        if ($definition->hasType()) {
105 1
            $collection = [];
106
107 1
            foreach ($value as $item) {
108 1
                $collection[] = $this->processTypeToResource($definition, $item);
109
            }
110
111 1
            return $collection;
112
        }
113
114 1
        return is_array($value) ? $value : iterator_to_array($value, false);
115
    }
116
117
118
    /**
119
     * Process data-type
120
     *
121
     * @param  Attribute $definition
122
     * @param  mixed     $value
123
     * @return mixed
124
     */
125 4 View Code Duplication
    protected function processTypeToResource(Attribute $definition, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
    {
127 4
        $type = $definition->getType();
128
129 4
        if (isset($this->registeredTypes[$type])) {
130 1
            $parameters = $definition->getTypeParameters();
131
132 1
            return $this->registeredTypes[$type]->toResource($value, $parameters);
133
        }
134
135 3
        if (in_array($type, $this->genericTypes)) {
136 2
            return $this->processGenericType($type, $value);
137
        }
138
139 1
        throw new \LogicException(sprintf('Unable to handle unknown type "%s" of attribute "%s"', $type, $definition->getName()));
140
    }
141
142
    /**
143
     * Process data-type
144
     *
145
     * @param  Attribute $definition
146
     * @param  mixed     $value
147
     * @return mixed
148
     */
149 3 View Code Duplication
    protected function processResourceToType(Attribute $definition, $value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
150
    {
151 3
        $type = $definition->getType();
152
153 3
        if (isset($this->registeredTypes[$type])) {
154 1
            $parameters = $definition->getTypeParameters();
155
156 1
            return $this->registeredTypes[$type]->fromResource($value, $parameters);
157
        }
158
159 2
        if (in_array($type, $this->genericTypes)) {
160 2
            return $this->processGenericType($type, $value);
161
        }
162
163
        throw new \LogicException(sprintf('Unable to handle unknown type "%s" of attribute "%s"', $type, $definition->getName()));
164
    }
165
166
    /**
167
     * Process generic data-types
168
     *
169
     * @param  string $type
170
     * @param  mixed  $value
171
     * @return bool|float|int|string
172
     */
173 4
    protected function processGenericType(string $type, $value)
174
    {
175 4
        if ($type === 'integer') {
176
            return (int) $value;
177
        }
178
179 4
        if ($type === 'float') {
180
            return (float) $value;
181
        }
182
183 4
        if ($type === 'boolean') {
184 2
            return (bool) $value;
185
        }
186
187 2
        return (string) $value;
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193 7
    public function fromResource($object, ResourceObject $resource, MappingContext $context)
194
    {
195 7
        $definitions = $context->getDefinition()->getAttributes();
196
197 7
        foreach ($definitions as $definition)
198
        {
199 7
            if (! $definition->hasSetter()) {
200
                continue;
201
            }
202
203 7
            $this->processResourceToAttribute($object, $resource, $definition);
204
        }
205 7
    }
206
207
    /**
208
     * Process resource to attribute mapping
209
     *
210
     * @param mixed          $object
211
     * @param ResourceObject $resource
212
     * @param Attribute      $definition
213
     */
214 7 View Code Duplication
    protected function processResourceToAttribute($object, ResourceObject $resource, Attribute $definition)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
    {
216 7
        $name = $definition->getName();
217
218 7
        if (! $resource->hasAttribute($name)) {
219
            return;
220
        }
221
222 7
        $value = $resource->getAttribute($name);
223
224 7
        if ($value === null && ! $definition->getProcessNull()) {
225 1
            return;
226
        }
227
228 6
        if ($definition->isMany()) {
229 2
            $this->processIterableValueFromResource($value, $object, $definition);
230 2
            return;
231
        }
232
233 4
        $setter = $definition->getSetter();
234
235 4
        if ($definition->hasType()) {
236 2
            $value = $this->processResourceToType($definition, $value);
237
        }
238
239 4
        $object->$setter($value);
240 4
    }
241
242
    /**
243
     * Process iterable value
244
     *
245
     * @param mixed     $value
246
     * @param mixed     $object
247
     * @param Attribute $definition
248
     */
249 2
    protected function processIterableValueFromResource($value, $object, Attribute $definition)
250
    {
251 2
        if (! $value instanceof \Traversable && ! is_array($value)) {
252
            throw new NotIterableAttribute($definition, $value);
253
        }
254
255 2
        $setter = $definition->getSetter();
256
257 2
        if ($definition->hasType()) {
258 1
            foreach ($value as $item) {
259 1
                $processedValue = $this->processResourceToType($definition, $item);
260
261 1
                $object->$setter($processedValue);
262
            }
263
264 1
            return;
265
        }
266
267 1
        foreach ($value as $item) {
268 1
            $object->$setter($item);
269
        }
270
    }
271
}