CanHydrate::hydrateEntity()   B
last analyzed

Complexity

Conditions 7
Paths 16

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 11
c 1
b 0
f 0
nc 16
nop 3
dl 0
loc 20
ccs 12
cts 12
cp 1
crap 7
rs 8.8333
1
<?php namespace Pz\Doctrine\Rest\Traits;
2
3
use Doctrine\Common\Collections\ArrayCollection;
4
use Doctrine\Common\Util\ClassUtils;
5
use Doctrine\ORM\Mapping\ClassMetadataInfo;
6
use Pz\Doctrine\Rest\Exceptions\RestException;
7
use Pz\Doctrine\Rest\RestRepository;
8
9
trait CanHydrate
10
{
11
    /**
12
     * @return RestRepository
13
     */
14
    abstract public function repository();
15
16
    /**
17
     * @param string|object $entity
18
     * @param array         $data
19
     * @param string        $scope
20
     *
21
     * @return object
22
     * @throws RestException
23
     */
24 8
    public function hydrateEntity($entity, array $data, $scope = '')
25
    {
26 8
        $hydrated = false;
27 8
        $entity = is_object($entity) ? $entity : new $entity;
28
29 8
        if (isset($data['attributes']) && is_array($data['attributes'])) {
30 8
            $entity = $this->hydrateAttributes($entity, $data['attributes'], $scope);
31 7
            $hydrated = true;
32
        }
33
34 8
        if (isset($data['relationships']) && is_array($data['relationships'])) {
35 3
            $entity = $this->hydrateRelationships($entity, $data['relationships'], $scope);
36 2
            $hydrated = true;
37
        }
38
39 8
        if (!$hydrated) {
40 1
            throw RestException::missingDataMembers($scope);
41
        }
42
43 7
        return $entity;
44
    }
45
46
    /**
47
     * @param        $entity
48
     * @param array  $attributes
49
     * @param string $scope
50
     *
51
     * @return mixed
52
     * @throws RestException
53
     */
54 8
    private function hydrateAttributes($entity, array $attributes, $scope = '')
55
    {
56 8
        $metadata = $this->repository()->getEntityManager()->getClassMetadata(ClassUtils::getClass($entity));
57
58 8
        foreach ($attributes as $name => $value) {
59 8
            if (!isset($metadata->reflFields[$name])) {
60 1
                throw RestException::unknownAttribute($scope.$name);
61
            }
62
63 7
            $this->setObjectProperty($entity, $name, $value);
64
        }
65
66 7
        return $entity;
67
    }
68
69
    /**
70
     * @param       $entity
71
     * @param array $relationships
72
     * @param       $scope
73
     *
74
     * @return mixed
75
     * @throws RestException
76
     */
77 3
    private function hydrateRelationships($entity, array $relationships, $scope)
78
    {
79 3
        $metadata = $this->repository()->getEntityManager()->getClassMetadata(ClassUtils::getClass($entity));
80
81 3
        foreach ($relationships as $name => $data) {
82 3
            if (!isset($metadata->associationMappings[$name])) {
83 1
                throw RestException::unknownRelation($scope.$name);
84
            }
85
86 3
            $mapping = $metadata->associationMappings[$name];
87
88 3
            if (!is_array($data) || !array_key_exists('data', $data)) {
89 1
                throw RestException::missingData($scope.$name);
90
            }
91
92 3
            if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
93 2
                $this->setObjectProperty($entity, $name,
94 2
                    $this->hydrateRelationData($mapping['targetEntity'], $data['data'], $scope.$name)
95
                );
96
            }
97
98 3
            if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY])) {
99 2
                $this->hydrateToManyRelation($entity, $name, $mapping['targetEntity'], $data['data'], $scope.$name);
100
            }
101
        }
102
103 2
        return $entity;
104
    }
105
106
    /**
107
     * Hydrate one relation.
108
     *
109
     * @param object $entity
110
     * @param string $name          Relation name
111
     * @param string $targetEntity  Doctrine relation class name
112
     * @param mixed  $data
113
     * @param string $scope
114
     *
115
     * @return object
116
     * @throws RestException
117
     */
118 2
    private function hydrateToManyRelation($entity, $name, $targetEntity, $data, $scope)
119
    {
120 2
        if (!is_array($data)) {
121 1
            throw RestException::missingData($scope);
122
        }
123
124 1
        $this->setObjectProperty($entity, $name,
125 1
            new ArrayCollection(array_map(
126 1
                function($item, $index) use ($targetEntity, $scope) {
127 1
                    return $this->hydrateRelationData($targetEntity, $item, $scope.'['.$index.']');
128 1
                },
129
                $data,
130 1
                array_keys($data)
131
            ))
132
        );
133 1
    }
134
135
    /**
136
     * @param string $class
137
     * @param mixed  $data
138
     * @param string $scope
139
     *
140
     * @return object
141
     * @throws RestException
142
     * @throws \Doctrine\ORM\ORMException
143
     */
144 2
    private function hydrateRelationData($class, $data, $scope)
145
    {
146 2
        if (is_null($data)) {
147
            return null;
148
        }
149
150 2
        if (is_object($data)) {
151
            return $data;
152
        }
153
154 2
        if (is_scalar($data)) {
155 1
            return $this->repository()->getEntityManager()->getReference($class, $data);
156
        }
157
158 2
        if (!is_array($data)) {
159
            throw RestException::missingData($scope);
160
        }
161
162 2
        if (isset($data['id']) && isset($data['type'])) {
163 2
            return $this->repository()->getEntityManager()->getReference($class, $data['id']);
164
        } else {
165 1
            return $this->hydrateEntity($class, $data, $scope.'.');
166
        }
167
    }
168
169
    /**
170
     * Set property on entity object.
171
     *
172
     * @param object $entity
173
     * @param string $name
174
     * @param mixed  $value
175
     *
176
     * @return object
177
     * @throws RestException
178
     */
179 8
    private function setObjectProperty($entity, $name, $value)
180
    {
181 8
        $setter = 'set'.ucfirst($name);
182
183 8
        if (!method_exists($entity, $setter)) {
184 1
            throw RestException::missingSetter($entity, $name, $setter);
185
        }
186
187 7
        return $entity->$setter($value);
188
    }
189
}
190