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

AttributeHandler::processTypeToResource()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 16
loc 16
c 0
b 0
f 0
ccs 8
cts 8
cp 1
rs 9.4285
cc 3
eloc 8
nc 3
nop 2
crap 3
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
}