Completed
Push — master ( 3f8257...005a8a )
by Jasper
10:02
created

ItemHydrator   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Test Coverage

Coverage 98.53%

Importance

Changes 0
Metric Value
wmc 25
eloc 56
dl 0
loc 199
ccs 67
cts 68
cp 0.9853
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A hydrateMorphToManyRelation() 0 9 3
A __construct() 0 3 1
A getRelationFromItem() 0 8 2
A hydrate() 0 6 1
B fillRelations() 0 20 7
A hydrateHasManyRelation() 0 10 3
A hydrateHasOneRelation() 0 12 2
A fill() 0 3 1
A hydrateMorphToRelation() 0 9 2
A buildRelationItem() 0 20 3
1
<?php
2
3
namespace Swis\JsonApi\Client;
4
5
use Swis\JsonApi\Client\Interfaces\ItemInterface;
6
use Swis\JsonApi\Client\Interfaces\RelationInterface;
7
use Swis\JsonApi\Client\Interfaces\TypeMapperInterface;
8
use Swis\JsonApi\Client\Relations\HasManyRelation;
9
use Swis\JsonApi\Client\Relations\HasOneRelation;
10
use Swis\JsonApi\Client\Relations\MorphToManyRelation;
11
use Swis\JsonApi\Client\Relations\MorphToRelation;
12
13
class ItemHydrator
14
{
15
    /**
16
     * @var \Swis\JsonApi\Client\Interfaces\TypeMapperInterface
17
     */
18
    protected $typeMapper;
19
20
    /**
21
     * @param \Swis\JsonApi\Client\Interfaces\TypeMapperInterface $typeMapper
22
     */
23 90
    public function __construct(TypeMapperInterface $typeMapper)
24
    {
25 90
        $this->typeMapper = $typeMapper;
26 90
    }
27
28
    /**
29
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
30
     * @param array                                         $attributes
31
     *
32
     * @throws \Exception
33
     *
34
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
35
     */
36 90
    public function hydrate(ItemInterface $item, array $attributes): ItemInterface
37
    {
38 90
        $this->fill($item, $attributes);
39 90
        $this->fillRelations($item, $attributes);
40
41 80
        return $item;
42
    }
43
44
    /**
45
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
46
     * @param array                                         $attributes
47
     */
48 90
    protected function fill(ItemInterface $item, array $attributes)
49
    {
50 90
        $item->fill(array_diff_key($attributes, array_combine($item->getAvailableRelations(), $item->getAvailableRelations())));
51 90
    }
52
53
    /**
54
     * Get relationships from the attributes and add them to the item.
55
     *
56
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
57
     * @param array                                         $attributes
58
     *
59
     * @throws \Exception
60
     */
61 90
    protected function fillRelations(ItemInterface $item, array $attributes)
62
    {
63
        // Fill Relations
64 90
        foreach ($item->getAvailableRelations() as $availableRelation) {
65 50
            if (!array_key_exists($availableRelation, $attributes)) {
66
                // No data found, continue
67 50
                continue;
68
            }
69
70 50
            $relation = $this->getRelationFromItem($item, $availableRelation);
71
72
            // It is a valid relation
73 50
            if ($relation instanceof HasOneRelation) {
74 10
                $this->hydrateHasOneRelation($item, $attributes, $relation, $availableRelation);
75 40
            } elseif ($relation instanceof HasManyRelation) {
76 10
                $this->hydrateHasManyRelation($attributes, $availableRelation, $relation);
77 30
            } elseif ($relation instanceof MorphToRelation) {
78 15
                $this->hydrateMorphToRelation($attributes, $relation, $availableRelation);
79 15
            } elseif ($relation instanceof MorphToManyRelation) {
80 45
                $this->hydrateMorphToManyRelation($attributes, $relation, $availableRelation);
81
            }
82
        }
83 80
    }
84
85
    /**
86
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
87
     * @param string                                        $availableRelation
88
     *
89
     * @throws \Exception
90
     *
91
     * @return \Swis\JsonApi\Client\Interfaces\RelationInterface
92
     */
93 50
    protected function getRelationFromItem(ItemInterface $item, string $availableRelation): RelationInterface
94
    {
95 50
        $method = camel_case($availableRelation);
96 50
        if (!method_exists($item, $method)) {
97
            throw new \Exception(sprintf('Method %s not found on %s', $method, get_class($item)));
98
        }
99
100 50
        return $item->$method();
101
    }
102
103
    /**
104
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
105
     * @param array                                         $attributes
106
     * @param \Swis\JsonApi\Client\Relations\HasOneRelation $relation
107
     * @param string                                        $availableRelation
108
     *
109
     * @throws \InvalidArgumentException
110
     */
111 10
    protected function hydrateHasOneRelation(
112
        ItemInterface $item,
113
        array $attributes,
114
        HasOneRelation $relation,
115
        string $availableRelation
116
    ) {
117 10
        if (is_array($attributes[$availableRelation])) {
118 5
            $relationItem = $this->buildRelationItem($relation, $attributes[$availableRelation]);
119 5
            $relation->associate($relationItem);
120
        } else {
121 10
            $relation->setId($attributes[$availableRelation]);
122 10
            $item->setAttribute($availableRelation.'_id', $attributes[$availableRelation]);
123
        }
124 10
    }
125
126
    /**
127
     * @param array                                          $attributes
128
     * @param string                                         $availableRelation
129
     * @param \Swis\JsonApi\Client\Relations\HasManyRelation $relation
130
     *
131
     * @throws \InvalidArgumentException
132
     */
133 10
    protected function hydrateHasManyRelation(array $attributes, string $availableRelation, HasManyRelation $relation)
134
    {
135 10
        foreach ($attributes[$availableRelation] as $relationData) {
136 10
            if (is_array($relationData)) {
137 5
                $relationItem = $this->buildRelationItem($relation, $relationData);
138
            } else {
139 5
                $relationItem = $this->buildRelationItem($relation, ['id' => $relationData]);
140
            }
141
142 10
            $relation->associate($relation->getIncluded()->push($relationItem));
143
        }
144 10
    }
145
146
    /**
147
     * @param array                                          $attributes
148
     * @param \Swis\JsonApi\Client\Relations\MorphToRelation $relation
149
     * @param string                                         $availableRelation
150
     *
151
     * @throws \InvalidArgumentException
152
     */
153 15
    protected function hydrateMorphToRelation(array $attributes, MorphToRelation $relation, string $availableRelation)
154
    {
155 15
        $relationData = $attributes[$availableRelation];
156 15
        if (!array_key_exists('type', $relationData)) {
157 5
            throw new \InvalidArgumentException('Always provide a "type" attribute in a morphTo relationship');
158
        }
159 10
        $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
160
161 10
        $relation->associate($relationItem);
162 10
    }
163
164
    /**
165
     * @param array                                              $attributes
166
     * @param \Swis\JsonApi\Client\Relations\MorphToManyRelation $relation
167
     * @param string                                             $availableRelation
168
     *
169
     * @throws \InvalidArgumentException
170
     */
171 15
    protected function hydrateMorphToManyRelation(array $attributes, MorphToManyRelation $relation, string $availableRelation)
172
    {
173 15
        foreach ($attributes[$availableRelation] as $relationData) {
174 15
            if (!array_key_exists('type', $relationData)) {
175 5
                throw new \InvalidArgumentException('Always provide a "type" attribute in a morphToMany relationship entry');
176
            }
177 10
            $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
178
179 10
            $relation->associate($relation->getIncluded()->push($relationItem));
180
        }
181 10
    }
182
183
    /**
184
     * @param \Swis\JsonApi\Client\Interfaces\RelationInterface $relation
185
     * @param array                                             $relationData
186
     * @param string|null                                       $type
187
     *
188
     * @throws \Exception
189
     *
190
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
191
     */
192 35
    protected function buildRelationItem(RelationInterface $relation, array $relationData, string $type = null): ItemInterface
193
    {
194
        // Sometimes the relatedType is provided from the relationship, but not always (i.e. Polymorphic Relationships)
195 35
        if (null === $type) {
196 15
            $type = $relation->getType();
197
        }
198
199 35
        if ($this->typeMapper->hasMapping($type)) {
200 25
            $relationItem = $this->typeMapper->getMapping($type);
201
        } else {
202 10
            $relationItem = new Item();
203 10
            $relationItem->setType($type);
204
        }
205
206 35
        $this->fill($relationItem, $relationData);
207 35
        $this->fillRelations($relationItem, $relationData);
208
209 35
        $relationItem->setId($relationData['id']);
210
211 35
        return $relationItem;
212
    }
213
}
214