Passed
Push — master ( 5574d5...24b486 )
by Jasper
04:36
created

ItemHydrator   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Test Coverage

Coverage 97.18%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 59
dl 0
loc 201
ccs 69
cts 71
cp 0.9718
rs 10
c 2
b 0
f 0
wmc 28

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A hydrateMorphToManyRelation() 0 9 3
A getRelationFromItem() 0 8 2
A hydrate() 0 10 3
B fillRelations() 0 20 7
A buildRelationItem() 0 23 4
A hydrateHasManyRelation() 0 10 3
A hydrateMorphToRelation() 0 9 2
A hydrateHasOneRelation() 0 9 2
A fill() 0 3 1
1
<?php
2
3
namespace Swis\JsonApi\Client;
4
5
use Illuminate\Support\Str;
6
use Swis\JsonApi\Client\Interfaces\ItemInterface;
7
use Swis\JsonApi\Client\Interfaces\TypedRelationInterface;
8
use Swis\JsonApi\Client\Interfaces\TypeMapperInterface;
9
use Swis\JsonApi\Client\Relations\HasManyRelation;
10
use Swis\JsonApi\Client\Relations\HasOneRelation;
11
use Swis\JsonApi\Client\Relations\MorphToManyRelation;
12
use Swis\JsonApi\Client\Relations\MorphToRelation;
13
14
class ItemHydrator
15
{
16
    /**
17
     * @var \Swis\JsonApi\Client\Interfaces\TypeMapperInterface
18
     */
19
    protected $typeMapper;
20
21
    /**
22
     * @param \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper
23
     */
24 130
    public function __construct(TypeMapperInterface $typeMapper)
25
    {
26 130
        $this->typeMapper = $typeMapper;
27 130
    }
28
29
    /**
30
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
31
     * @param array                                         $attributes
32
     * @param string|null                                   $id
33
     *
34
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
35
     */
36 130
    public function hydrate(ItemInterface $item, array $attributes, string $id = null): ItemInterface
37
    {
38 130
        $this->fill($item, $attributes);
39 130
        $this->fillRelations($item, $attributes);
40
41 120
        if ($id !== null && $id !== '') {
42 35
            $item->setId($id);
43
        }
44
45 120
        return $item;
46
    }
47
48
    /**
49
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
50
     * @param array                                         $attributes
51
     */
52 130
    protected function fill(ItemInterface $item, array $attributes)
53
    {
54 130
        $item->fill(array_diff_key($attributes, array_combine($item->getAvailableRelations(), $item->getAvailableRelations())));
0 ignored issues
show
Bug introduced by
It seems like array_combine($item->get...etAvailableRelations()) can also be of type false; however, parameter $array2 of array_diff_key() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

54
        $item->fill(array_diff_key($attributes, /** @scrutinizer ignore-type */ array_combine($item->getAvailableRelations(), $item->getAvailableRelations())));
Loading history...
55 130
    }
56
57
    /**
58
     * Get relationships from the attributes and add them to the item.
59
     *
60
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
61
     * @param array                                         $attributes
62
     *
63
     * @throws \RuntimeException
64
     */
65 130
    protected function fillRelations(ItemInterface $item, array $attributes)
66
    {
67
        // Fill Relations
68 130
        foreach ($item->getAvailableRelations() as $availableRelation) {
69 65
            if (!array_key_exists($availableRelation, $attributes)) {
70
                // No data found, continue
71 65
                continue;
72
            }
73
74 65
            $relation = $this->getRelationFromItem($item, $availableRelation);
75
76
            // It is a valid relation
77 65
            if ($relation instanceof HasOneRelation) {
78 15
                $this->hydrateHasOneRelation($attributes, $relation, $availableRelation);
79 50
            } elseif ($relation instanceof HasManyRelation) {
80 10
                $this->hydrateHasManyRelation($attributes, $relation, $availableRelation);
81 40
            } elseif ($relation instanceof MorphToRelation) {
82 20
                $this->hydrateMorphToRelation($attributes, $relation, $availableRelation);
83 20
            } elseif ($relation instanceof MorphToManyRelation) {
84 28
                $this->hydrateMorphToManyRelation($attributes, $relation, $availableRelation);
85
            }
86
        }
87 120
    }
88
89
    /**
90
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
91
     * @param string                                        $availableRelation
92
     *
93
     * @throws \RuntimeException
94
     *
95
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface
96
     */
97 65
    protected function getRelationFromItem(ItemInterface $item, string $availableRelation)
98
    {
99 65
        $method = Str::camel($availableRelation);
100 65
        if (!method_exists($item, $method)) {
101
            throw new \RuntimeException(sprintf('Method %s not found on %s', $method, get_class($item)));
102
        }
103
104 65
        return $item->$method();
105
    }
106
107
    /**
108
     * @param array                                         $attributes
109
     * @param \Swis\JsonApi\Client\Relations\HasOneRelation $relation
110
     * @param string                                        $availableRelation
111
     *
112
     * @throws \InvalidArgumentException
113
     */
114 15
    protected function hydrateHasOneRelation(array $attributes, HasOneRelation $relation, string $availableRelation)
115
    {
116 15
        if (is_array($attributes[$availableRelation])) {
117 10
            $relationItem = $this->buildRelationItem($relation, $attributes[$availableRelation]);
118
        } else {
119 10
            $relationItem = $this->buildRelationItem($relation, ['id' => $attributes[$availableRelation]]);
120
        }
121
122 15
        $relation->associate($relationItem);
123 15
    }
124
125
    /**
126
     * @param array                                          $attributes
127
     * @param string                                         $availableRelation
128
     * @param \Swis\JsonApi\Client\Relations\HasManyRelation $relation
129
     *
130
     * @throws \InvalidArgumentException
131
     */
132 10
    protected function hydrateHasManyRelation(array $attributes, HasManyRelation $relation, string $availableRelation)
133
    {
134 10
        foreach ($attributes[$availableRelation] as $relationData) {
135 10
            if (is_array($relationData)) {
136 5
                $relationItem = $this->buildRelationItem($relation, $relationData);
137
            } else {
138 5
                $relationItem = $this->buildRelationItem($relation, ['id' => $relationData]);
139
            }
140
141 10
            $relation->associate($relation->getIncluded()->push($relationItem));
142
        }
143 10
    }
144
145
    /**
146
     * @param array                                          $attributes
147
     * @param \Swis\JsonApi\Client\Relations\MorphToRelation $relation
148
     * @param string                                         $availableRelation
149
     *
150
     * @throws \InvalidArgumentException
151
     */
152 20
    protected function hydrateMorphToRelation(array $attributes, MorphToRelation $relation, string $availableRelation)
153
    {
154 20
        $relationData = $attributes[$availableRelation];
155 20
        if (!array_key_exists('type', $relationData)) {
156 5
            throw new \InvalidArgumentException('Always provide a "type" attribute in a morphTo relationship');
157
        }
158 15
        $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
159
160 15
        $relation->associate($relationItem);
161 15
    }
162
163
    /**
164
     * @param array                                              $attributes
165
     * @param \Swis\JsonApi\Client\Relations\MorphToManyRelation $relation
166
     * @param string                                             $availableRelation
167
     *
168
     * @throws \InvalidArgumentException
169
     */
170 20
    protected function hydrateMorphToManyRelation(array $attributes, MorphToManyRelation $relation, string $availableRelation)
171
    {
172 20
        foreach ($attributes[$availableRelation] as $relationData) {
173 20
            if (!array_key_exists('type', $relationData)) {
174 5
                throw new \InvalidArgumentException('Always provide a "type" attribute in a morphToMany relationship entry');
175
            }
176 15
            $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
177
178 15
            $relation->associate($relation->getIncluded()->push($relationItem));
179
        }
180 15
    }
181
182
    /**
183
     * @param \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relation
184
     * @param array                                                                                                      $relationData
185
     * @param string|null                                                                                                $type
186
     *
187
     * @throws \InvalidArgumentException
188
     * @throws \RuntimeException
189
     *
190
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
191
     */
192 55
    protected function buildRelationItem($relation, array $relationData, string $type = null): ItemInterface
193
    {
194 55
        if (null === $type) {
195 25
            if (!$relation instanceof TypedRelationInterface) {
196
                throw new \InvalidArgumentException('Param $type is required when the relation is not typed');
197
            }
198
199 25
            $type = $relation->getType();
200
        }
201
202 55
        if ($this->typeMapper->hasMapping($type)) {
203 45
            $relationItem = $this->typeMapper->getMapping($type);
204
        } else {
205 15
            $relationItem = new Item();
206 15
            $relationItem->setType($type);
207
        }
208
209 55
        $this->fill($relationItem, $relationData);
210 55
        $this->fillRelations($relationItem, $relationData);
211
212 55
        $relationItem->setId($relationData['id']);
213
214 55
        return $relationItem;
215
    }
216
}
217