Completed
Push — master ( 527079...b8d1ae )
by Jasper
15s
created

ItemHydrator::hydrateHasManyRelation()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
c 0
b 0
f 0
ccs 6
cts 6
cp 1
rs 10
cc 3
nc 3
nop 3
crap 3
1
<?php
2
3
namespace Swis\JsonApi\Client;
4
5
use Swis\JsonApi\Client\Interfaces\ItemInterface;
6
use Swis\JsonApi\Client\Interfaces\TypedRelationInterface;
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 105
    public function __construct(TypeMapperInterface $typeMapper)
24
    {
25 105
        $this->typeMapper = $typeMapper;
26 105
    }
27
28
    /**
29
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
30
     * @param array                                         $attributes
31
     *
32
     * @throws \RuntimeException
33
     *
34
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
35
     */
36 105
    public function hydrate(ItemInterface $item, array $attributes): ItemInterface
37
    {
38 105
        $this->fill($item, $attributes);
39 105
        $this->fillRelations($item, $attributes);
40
41 95
        return $item;
42
    }
43
44
    /**
45
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
46
     * @param array                                         $attributes
47
     */
48 105
    protected function fill(ItemInterface $item, array $attributes)
49
    {
50 105
        $item->fill(array_diff_key($attributes, array_combine($item->getAvailableRelations(), $item->getAvailableRelations())));
51 105
    }
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 \RuntimeException
60
     */
61 105
    protected function fillRelations(ItemInterface $item, array $attributes)
62
    {
63
        // Fill Relations
64 105
        foreach ($item->getAvailableRelations() as $availableRelation) {
65 65
            if (!array_key_exists($availableRelation, $attributes)) {
66
                // No data found, continue
67 65
                continue;
68
            }
69
70 65
            $relation = $this->getRelationFromItem($item, $availableRelation);
71
72
            // It is a valid relation
73 65
            if ($relation instanceof HasOneRelation) {
74 15
                $this->hydrateHasOneRelation($attributes, $relation, $availableRelation);
75 50
            } elseif ($relation instanceof HasManyRelation) {
76 10
                $this->hydrateHasManyRelation($attributes, $relation, $availableRelation);
77 40
            } elseif ($relation instanceof MorphToRelation) {
78 20
                $this->hydrateMorphToRelation($attributes, $relation, $availableRelation);
79 20
            } elseif ($relation instanceof MorphToManyRelation) {
80 36
                $this->hydrateMorphToManyRelation($attributes, $relation, $availableRelation);
81
            }
82
        }
83 95
    }
84
85
    /**
86
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
87
     * @param string                                        $availableRelation
88
     *
89
     * @throws \RuntimeException
90
     *
91
     * @return \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface
92
     */
93 65
    protected function getRelationFromItem(ItemInterface $item, string $availableRelation)
94
    {
95 65
        $method = camel_case($availableRelation);
96 65
        if (!method_exists($item, $method)) {
97
            throw new \RuntimeException(sprintf('Method %s not found on %s', $method, get_class($item)));
98
        }
99
100 65
        return $item->$method();
101
    }
102
103
    /**
104
     * @param array                                         $attributes
105
     * @param \Swis\JsonApi\Client\Relations\HasOneRelation $relation
106
     * @param string                                        $availableRelation
107
     *
108
     * @throws \InvalidArgumentException
109
     */
110 15
    protected function hydrateHasOneRelation(array $attributes, HasOneRelation $relation, string $availableRelation)
111
    {
112 15
        if (is_array($attributes[$availableRelation])) {
113 10
            $relationItem = $this->buildRelationItem($relation, $attributes[$availableRelation]);
114
        } else {
115 10
            $relationItem = $this->buildRelationItem($relation, ['id' => $attributes[$availableRelation]]);
116
        }
117
118 15
        $relation->associate($relationItem);
119 15
    }
120
121
    /**
122
     * @param array                                          $attributes
123
     * @param string                                         $availableRelation
124
     * @param \Swis\JsonApi\Client\Relations\HasManyRelation $relation
125
     *
126
     * @throws \InvalidArgumentException
127
     */
128 10
    protected function hydrateHasManyRelation(array $attributes, HasManyRelation $relation, string $availableRelation)
129
    {
130 10
        foreach ($attributes[$availableRelation] as $relationData) {
131 10
            if (is_array($relationData)) {
132 5
                $relationItem = $this->buildRelationItem($relation, $relationData);
133
            } else {
134 5
                $relationItem = $this->buildRelationItem($relation, ['id' => $relationData]);
135
            }
136
137 10
            $relation->associate($relation->getIncluded()->push($relationItem));
138
        }
139 10
    }
140
141
    /**
142
     * @param array                                          $attributes
143
     * @param \Swis\JsonApi\Client\Relations\MorphToRelation $relation
144
     * @param string                                         $availableRelation
145
     *
146
     * @throws \InvalidArgumentException
147
     */
148 20
    protected function hydrateMorphToRelation(array $attributes, MorphToRelation $relation, string $availableRelation)
149
    {
150 20
        $relationData = $attributes[$availableRelation];
151 20
        if (!array_key_exists('type', $relationData)) {
152 5
            throw new \InvalidArgumentException('Always provide a "type" attribute in a morphTo relationship');
153
        }
154 15
        $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
155
156 15
        $relation->associate($relationItem);
157 15
    }
158
159
    /**
160
     * @param array                                              $attributes
161
     * @param \Swis\JsonApi\Client\Relations\MorphToManyRelation $relation
162
     * @param string                                             $availableRelation
163
     *
164
     * @throws \InvalidArgumentException
165
     */
166 20
    protected function hydrateMorphToManyRelation(array $attributes, MorphToManyRelation $relation, string $availableRelation)
167
    {
168 20
        foreach ($attributes[$availableRelation] as $relationData) {
169 20
            if (!array_key_exists('type', $relationData)) {
170 5
                throw new \InvalidArgumentException('Always provide a "type" attribute in a morphToMany relationship entry');
171
            }
172 15
            $relationItem = $this->buildRelationItem($relation, array_diff_key($relationData, ['type' => 'type']), $relationData['type']);
173
174 15
            $relation->associate($relation->getIncluded()->push($relationItem));
175
        }
176 15
    }
177
178
    /**
179
     * @param \Swis\JsonApi\Client\Interfaces\OneRelationInterface|\Swis\JsonApi\Client\Interfaces\ManyRelationInterface $relation
180
     * @param array                                                                                                      $relationData
181
     * @param string|null                                                                                                $type
182
     *
183
     * @throws \InvalidArgumentException
184
     * @throws \RuntimeException
185
     *
186
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
187
     */
188 55
    protected function buildRelationItem($relation, array $relationData, string $type = null): ItemInterface
189
    {
190 55
        if (null === $type) {
191 25
            if (!$relation instanceof TypedRelationInterface) {
192
                throw new \InvalidArgumentException('Param $type is required when the relation is not typed');
193
            }
194
195 25
            $type = $relation->getType();
196
        }
197
198 55
        if ($this->typeMapper->hasMapping($type)) {
199 45
            $relationItem = $this->typeMapper->getMapping($type);
200
        } else {
201 15
            $relationItem = new Item();
202 15
            $relationItem->setType($type);
203
        }
204
205 55
        $this->fill($relationItem, $relationData);
206 55
        $this->fillRelations($relationItem, $relationData);
207
208 55
        $relationItem->setId($relationData['id']);
209
210 55
        return $relationItem;
211
    }
212
}
213