Completed
Push — master ( 1f65b1...554598 )
by Jasper
05:20
created

ItemHydrator::fillRelations()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7

Importance

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