Completed
Push — master ( 2d1166...6a069e )
by Jasper
03:24
created

ItemHydrator::buildRelationItem()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4.0072

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 23
ccs 12
cts 13
cp 0.9231
rs 9.8333
c 0
b 0
f 0
cc 4
nc 5
nop 3
crap 4.0072
1
<?php
2
3
namespace Swis\JsonApi\Client;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use Swis\JsonApi\Client\Interfaces\ItemInterface;
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 95
    public function __construct(TypeMapperInterface $typeMapper)
25
    {
26 95
        $this->typeMapper = $typeMapper;
27 95
    }
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 95
    public function hydrate(ItemInterface $item, array $attributes, string $id = null): ItemInterface
37
    {
38 95
        $this->fill($item, $attributes);
39 95
        $this->fillRelations($item, $attributes);
40
41 85
        if ($id !== null && $id !== '') {
42 70
            $item->setId($id);
43
        }
44
45 85
        return $item;
46
    }
47
48
    /**
49
     * @param \Swis\JsonApi\Client\Interfaces\ItemInterface $item
50
     * @param array                                         $attributes
51
     */
52 95
    protected function fill(ItemInterface $item, array $attributes): void
53
    {
54 95
        $item->fill(Arr::except($attributes, $item->getAvailableRelations()));
55 95
    }
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 95
    protected function fillRelations(ItemInterface $item, array $attributes): void
66
    {
67
        // Fill Relations
68 95
        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($relation, $attributes[$availableRelation]);
79 50
            } elseif ($relation instanceof HasManyRelation) {
80 10
                $this->hydrateHasManyRelation($relation, $attributes[$availableRelation]);
81 40
            } elseif ($relation instanceof MorphToRelation) {
82 20
                $this->hydrateMorphToRelation($relation, $attributes[$availableRelation]);
83 20
            } elseif ($relation instanceof MorphToManyRelation) {
84 28
                $this->hydrateMorphToManyRelation($relation, $attributes[$availableRelation]);
85
            }
86
        }
87 85
    }
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 \Swis\JsonApi\Client\Relations\HasOneRelation $relation
109
     * @param array|string                                  $attributes
110
     *
111
     * @throws \InvalidArgumentException
112
     */
113 15
    protected function hydrateHasOneRelation(HasOneRelation $relation, $attributes): void
114
    {
115 15
        if (is_array($attributes)) {
116 10
            $relationItem = $this->buildItem($relation->getType(), $attributes);
117
        } else {
118 10
            $relationItem = $this->buildItem($relation->getType(), ['id' => $attributes]);
119
        }
120
121 15
        $relation->associate($relationItem);
122 15
    }
123
124
    /**
125
     * @param \Swis\JsonApi\Client\Relations\HasManyRelation $relation
126
     * @param array                                          $attributes
127
     *
128
     * @throws \InvalidArgumentException
129
     */
130 10
    protected function hydrateHasManyRelation(HasManyRelation $relation, array $attributes): void
131
    {
132 10
        foreach ($attributes as $relationData) {
133 10
            if (is_array($relationData)) {
134 5
                $relationItem = $this->buildItem($relation->getType(), $relationData);
135
            } else {
136 5
                $relationItem = $this->buildItem($relation->getType(), ['id' => $relationData]);
137
            }
138
139 10
            $relation->associate($relation->getIncluded()->push($relationItem));
140
        }
141 10
    }
142
143
    /**
144
     * @param \Swis\JsonApi\Client\Relations\MorphToRelation $relation
145
     * @param array                                          $attributes
146
     *
147
     * @throws \InvalidArgumentException
148
     */
149 20
    protected function hydrateMorphToRelation(MorphToRelation $relation, array $attributes): void
150
    {
151 20
        if (!array_key_exists('type', $attributes)) {
152 5
            throw new \InvalidArgumentException('Always provide a "type" attribute in a morphTo relationship');
153
        }
154 15
        $relationItem = $this->buildItem($attributes['type'], Arr::except($attributes, 'type'));
155
156 15
        $relation->associate($relationItem);
157 15
    }
158
159
    /**
160
     * @param \Swis\JsonApi\Client\Relations\MorphToManyRelation $relation
161
     * @param array                                              $attributes
162
     *
163
     * @throws \InvalidArgumentException
164
     */
165 20
    protected function hydrateMorphToManyRelation(MorphToManyRelation $relation, array $attributes): void
166
    {
167 20
        foreach ($attributes as $relationData) {
168 20
            if (!array_key_exists('type', $relationData)) {
169 5
                throw new \InvalidArgumentException('Always provide a "type" attribute in a morphToMany relationship entry');
170
            }
171 15
            $relationItem = $this->buildItem($relationData['type'], Arr::except($relationData, 'type'));
172
173 15
            $relation->associate($relation->getIncluded()->push($relationItem));
174
        }
175 15
    }
176
177
    /**
178
     * @param string $type
179
     * @param array  $attributes
180
     *
181
     * @throws \InvalidArgumentException
182
     * @throws \RuntimeException
183
     *
184
     * @return \Swis\JsonApi\Client\Interfaces\ItemInterface
185
     */
186 55
    protected function buildItem(string $type, array $attributes): ItemInterface
187
    {
188 55
        $item = (new Item())->setType($type);
189 55
        if ($this->typeMapper->hasMapping($type)) {
190 45
            $item = $this->typeMapper->getMapping($type);
191
        }
192
193 55
        return $this->hydrate($item, $attributes, $attributes['id']);
194
    }
195
}
196