ItemParser   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 43
eloc 76
c 1
b 1
f 0
dl 0
loc 154
ccs 79
cts 79
cp 1
rs 8.96

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getItemInstance() 0 7 2
C setRelations() 0 39 14
C parse() 0 46 17
B parseRelationshipData() 0 29 9

How to fix   Complexity   

Complex Class

Complex classes like ItemParser 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.

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 ItemParser, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Swis\JsonApi\Client\Parsers;
6
7
use Swis\JsonApi\Client\Collection;
8
use Swis\JsonApi\Client\Exceptions\ValidationException;
9
use Swis\JsonApi\Client\Interfaces\DataInterface;
10
use Swis\JsonApi\Client\Interfaces\ItemInterface;
11
use Swis\JsonApi\Client\Interfaces\TypeMapperInterface;
12
use Swis\JsonApi\Client\Item;
13
14
/**
15
 * @internal
16
 */
17
class ItemParser
18
{
19
    private TypeMapperInterface $typeMapper;
20
21
    private LinksParser $linksParser;
22
23
    private MetaParser $metaParser;
24
25 444
    public function __construct(TypeMapperInterface $typeMapper, LinksParser $linksParser, MetaParser $metaParser)
26
    {
27 444
        $this->typeMapper = $typeMapper;
28 444
        $this->linksParser = $linksParser;
29 444
        $this->metaParser = $metaParser;
30 222
    }
31
32
    /**
33
     * @param  mixed  $data
34
     */
35 328
    public function parse($data): ItemInterface
36
    {
37 328
        if (! is_object($data)) {
38 24
            throw new ValidationException(sprintf('Resource MUST be an object, "%s" given.', gettype($data)));
39
        }
40 304
        if (! property_exists($data, 'type')) {
41 4
            throw new ValidationException('Resource object MUST contain a type.');
42
        }
43 300
        if (! property_exists($data, 'id')) {
44 4
            throw new ValidationException('Resource object MUST contain an id.');
45
        }
46 296
        if (! is_string($data->type)) {
47 24
            throw new ValidationException(sprintf('Resource property "type" MUST be a string, "%s" given.', gettype($data->type)));
48
        }
49 272
        if (! is_string($data->id) && ! is_numeric($data->id)) {
50 16
            throw new ValidationException(sprintf('Resource property "id" MUST be a string, "%s" given.', gettype($data->id)));
51
        }
52 256
        if (property_exists($data, 'attributes')) {
53 132
            if (! is_object($data->attributes)) {
54 24
                throw new ValidationException(sprintf('Resource property "attributes" MUST be an object, "%s" given.', gettype($data->attributes)));
55
            }
56 108
            if (property_exists($data->attributes, 'type') || property_exists($data->attributes, 'id') || property_exists($data->attributes, 'relationships') || property_exists($data->attributes, 'links')) {
57 16
                throw new ValidationException('These properties are not allowed in attributes: `type`, `id`, `relationships`, `links`.');
58
            }
59
        }
60
61 216
        $item = $this->getItemInstance($data->type);
62 216
        $item->setId((string) $data->id);
63
64 216
        if (property_exists($data, 'attributes')) {
65 92
            $item->fill((array) $data->attributes);
66
        }
67
68 216
        if (property_exists($data, 'relationships')) {
69 200
            $this->setRelations($item, $data->relationships);
70
        }
71
72 88
        if (property_exists($data, 'links')) {
73 52
            $item->setLinks($this->linksParser->parse($data->links, LinksParser::SOURCE_ITEM));
74
        }
75
76 88
        if (property_exists($data, 'meta')) {
77 56
            $item->setMeta($this->metaParser->parse($data->meta));
78
        }
79
80 88
        return $item;
81
    }
82
83 216
    private function getItemInstance(string $type): ItemInterface
84
    {
85 216
        if ($this->typeMapper->hasMapping($type)) {
86 192
            return $this->typeMapper->getMapping($type);
87
        }
88
89 24
        return (new Item)->setType($type);
90
    }
91
92
    /**
93
     * @param  mixed  $data
94
     */
95 200
    private function setRelations(ItemInterface $item, $data): void
96
    {
97 200
        if (! is_object($data)) {
98 24
            throw new ValidationException(sprintf('Resource property "relationships" MUST be an object, "%s" given.', gettype($data)));
99
        }
100 176
        if (property_exists($data, 'type') || property_exists($data, 'id')) {
101 8
            throw new ValidationException('These properties are not allowed in relationships: `type`, `id`.');
102
        }
103
104 168
        foreach ($data as $name => $relationship) {
105 168
            if ($item->hasAttribute($name)) {
106 4
                throw new ValidationException(sprintf('Relationship "%s" cannot be set because it already exists in Resource object.', $name));
107
            }
108 164
            if (! is_object($relationship)) {
109 24
                throw new ValidationException(sprintf('Relationship MUST be an object, "%s" given.', gettype($relationship)));
110
            }
111 140
            if (! property_exists($relationship, 'links') && ! property_exists($relationship, 'data') && ! property_exists($relationship, 'meta')) {
112 4
                throw new ValidationException('Relationship object MUST contain at least one of the following properties: `links`, `data`, `meta`.');
113
            }
114
115 136
            $value = false;
116 136
            if (property_exists($relationship, 'data')) {
117 136
                $value = null;
118 136
                if ($relationship->data !== null) {
119 132
                    $value = $this->parseRelationshipData($relationship->data);
120
                }
121
            }
122
123 72
            $links = null;
124 72
            if (property_exists($relationship, 'links')) {
125 52
                $links = $this->linksParser->parse($relationship->links, LinksParser::SOURCE_RELATIONSHIP);
126
            }
127
128 72
            $meta = null;
129 72
            if (property_exists($relationship, 'meta')) {
130 52
                $meta = $this->metaParser->parse($relationship->meta);
131
            }
132
133 72
            $item->setRelation($name, $value, $links, $meta);
134
        }
135 36
    }
136
137
    /**
138
     * @param  mixed  $data
139
     *
140
     * @throws \InvalidArgumentException
141
     */
142 132
    private function parseRelationshipData($data): DataInterface
143
    {
144 132
        if (is_array($data)) {
145 60
            return Collection::make($data)
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
            return Collection::make(/** @scrutinizer ignore-type */ $data)
Loading history...
146 60
                ->map(fn ($identifier) => $this->parseRelationshipData($identifier));
147
        }
148
149 128
        if (! is_object($data)) {
150 16
            throw new ValidationException(sprintf('ResourceIdentifier MUST be an object, "%s" given.', gettype($data)));
151
        }
152 112
        if (! property_exists($data, 'type')) {
153 4
            throw new ValidationException('ResourceIdentifier object MUST contain a type.');
154
        }
155 108
        if (! property_exists($data, 'id')) {
156 4
            throw new ValidationException('ResourceIdentifier object MUST contain an id.');
157
        }
158 104
        if (! is_string($data->type)) {
159 24
            throw new ValidationException(sprintf('ResourceIdentifier property "type" MUST be a string, "%s" given.', gettype($data->type)));
160
        }
161 80
        if (! is_string($data->id) && ! is_numeric($data->id)) {
162 16
            throw new ValidationException(sprintf('ResourceIdentifier property "id" MUST be a string, "%s" given.', gettype($data->id)));
163
        }
164
165 64
        $item = $this->getItemInstance($data->type)->setId((string) $data->id);
166 64
        if (property_exists($data, 'meta')) {
167 56
            $item->setMeta($this->metaParser->parse($data->meta));
168
        }
169
170 64
        return $item;
171
    }
172
}
173