ItemHydrator::hydrateHasOneRelation()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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