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
|
6 |
|
public function hydrateEntity($entity, array $data, $scope = '') |
25
|
|
|
{ |
26
|
6 |
|
$hydrated = false; |
27
|
6 |
|
$entity = is_object($entity) ? $entity : new $entity; |
28
|
|
|
|
29
|
6 |
View Code Duplication |
if (isset($data['attributes']) && is_array($data['attributes'])) { |
|
|
|
|
30
|
6 |
|
$entity = $this->hydrateAttributes($entity, $data['attributes'], $scope); |
31
|
5 |
|
$hydrated = true; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
|
35
|
6 |
View Code Duplication |
if (isset($data['relationships']) && is_array($data['relationships'])) { |
|
|
|
|
36
|
3 |
|
$entity = $this->hydrateRelationships($entity, $data['relationships'], $scope); |
37
|
2 |
|
$hydrated = true; |
38
|
|
|
} |
39
|
|
|
|
40
|
6 |
|
if (!$hydrated) { |
41
|
1 |
|
throw RestException::missingDataMembers($scope); |
42
|
|
|
} |
43
|
|
|
|
44
|
5 |
|
return $entity; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param $entity |
49
|
|
|
* @param array $attributes |
50
|
|
|
* @param string $scope |
51
|
|
|
* |
52
|
|
|
* @return mixed |
53
|
|
|
* @throws RestException |
54
|
|
|
*/ |
55
|
6 |
|
private function hydrateAttributes($entity, array $attributes, $scope = '') |
56
|
|
|
{ |
57
|
6 |
|
$metadata = $this->repository()->getEntityManager()->getClassMetadata(ClassUtils::getClass($entity)); |
58
|
|
|
|
59
|
6 |
|
foreach ($attributes as $name => $value) { |
60
|
6 |
|
if (!isset($metadata->reflFields[$name])) { |
61
|
1 |
|
throw RestException::unknownAttribute($scope.$name); |
62
|
|
|
} |
63
|
|
|
|
64
|
5 |
|
$this->setProperty($entity, $name, $value); |
65
|
|
|
} |
66
|
|
|
|
67
|
5 |
|
return $entity; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param $entity |
72
|
|
|
* @param array $relationships |
73
|
|
|
* @param $scope |
74
|
|
|
* |
75
|
|
|
* @return mixed |
76
|
|
|
* @throws RestException |
77
|
|
|
*/ |
78
|
3 |
|
private function hydrateRelationships($entity, array $relationships, $scope) |
79
|
|
|
{ |
80
|
3 |
|
$metadata = $this->repository()->getEntityManager()->getClassMetadata(ClassUtils::getClass($entity)); |
81
|
|
|
|
82
|
3 |
|
foreach ($relationships as $name => $data) { |
83
|
3 |
|
if (!isset($metadata->associationMappings[$name])) { |
84
|
1 |
|
throw RestException::unknownRelation($scope.$name); |
85
|
|
|
} |
86
|
|
|
|
87
|
3 |
|
$mapping = $metadata->associationMappings[$name]; |
88
|
|
|
|
89
|
3 |
|
if (!isset($data['data'])) { |
90
|
1 |
|
throw RestException::missingData($scope.$name); |
91
|
|
|
} |
92
|
|
|
|
93
|
3 |
|
if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) { |
94
|
3 |
|
$this->setProperty($entity, $name, |
95
|
3 |
|
$this->hydrateRelationData($mapping['targetEntity'], $data['data'], $scope.$name) |
96
|
|
|
); |
97
|
|
|
} |
98
|
|
|
|
99
|
3 |
|
if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY])) { |
100
|
3 |
|
$this->hydrateToManyRelation($entity, $name, $mapping['targetEntity'], $data['data'], $scope.$name); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
2 |
|
return $entity; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Hydrate one relation. |
109
|
|
|
* |
110
|
|
|
* @param object $entity |
111
|
|
|
* @param string $name Relation name |
112
|
|
|
* @param array $targetEntity Doctrine relation class name |
113
|
|
|
* @param mixed $data |
114
|
|
|
* @param string $scope |
115
|
|
|
* |
116
|
|
|
* @return object |
117
|
|
|
* @throws RestException |
118
|
|
|
*/ |
119
|
2 |
|
private function hydrateToManyRelation($entity, $name, $targetEntity, $data, $scope) |
120
|
|
|
{ |
121
|
2 |
|
if (!is_array($data)) { |
122
|
1 |
|
throw RestException::missingData($scope); |
123
|
|
|
} |
124
|
|
|
|
125
|
1 |
|
$this->setProperty($entity, $name, |
126
|
1 |
|
new ArrayCollection(array_map( |
127
|
1 |
|
function($item, $index) use ($targetEntity, $name, $scope) { |
|
|
|
|
128
|
1 |
|
return $this->hydrateRelationData($targetEntity, $item, $scope.'['.$index.']'); |
|
|
|
|
129
|
1 |
|
}, |
130
|
1 |
|
$data, |
131
|
1 |
|
array_keys($data) |
132
|
|
|
)) |
133
|
|
|
); |
134
|
1 |
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @param string $class |
138
|
|
|
* @param mixed $data |
139
|
|
|
* @param string $scope |
140
|
|
|
* |
141
|
|
|
* @return object |
142
|
|
|
* @throws RestException |
143
|
|
|
* @throws \Doctrine\ORM\ORMException |
144
|
|
|
*/ |
145
|
3 |
|
private function hydrateRelationData($class, $data, $scope) |
146
|
|
|
{ |
147
|
3 |
|
if (is_scalar($data)) { |
148
|
1 |
|
return $this->repository()->getEntityManager()->getReference($class, $data); |
149
|
|
|
} |
150
|
|
|
|
151
|
3 |
|
if (!is_array($data)) { |
152
|
1 |
|
throw RestException::missingData($scope); |
153
|
|
|
} |
154
|
|
|
|
155
|
2 |
|
if (isset($data['id']) && isset($data['type'])) { |
156
|
2 |
|
return $this->repository()->getEntityManager()->getReference($class, $data['id']); |
157
|
|
|
} else { |
158
|
1 |
|
return $this->hydrateEntity($class, $data, $scope.'.'); |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Set property on entity object. |
164
|
|
|
* |
165
|
|
|
* @param object $entity |
166
|
|
|
* @param string $name |
167
|
|
|
* @param mixed $value |
168
|
|
|
* |
169
|
|
|
* @return object |
170
|
|
|
* @throws RestException |
171
|
|
|
*/ |
172
|
6 |
|
private function setProperty($entity, $name, $value) |
173
|
|
|
{ |
174
|
6 |
|
$setter = 'set'.ucfirst($name); |
175
|
|
|
|
176
|
6 |
|
if (!method_exists($entity, $setter)) { |
177
|
1 |
|
$source = ['pointer' => $name, 'entity' => ClassUtils::getClass($entity), 'setter' => $setter]; |
178
|
1 |
|
throw RestException::createUnprocessable('Setter not found for entity') |
179
|
1 |
|
->error('missing-setter', $source, 'Missing field setter.'); |
180
|
|
|
} |
181
|
|
|
|
182
|
5 |
|
$entity->$setter($value); |
183
|
|
|
|
184
|
5 |
|
return $entity; |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.