These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* |
||
3 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
4 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
5 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
6 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
7 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
8 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
9 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
10 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
11 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
12 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
13 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
14 | * |
||
15 | * This software consists of voluntary contributions made by many individuals |
||
16 | * and is licensed under the MIT license. For more information, see |
||
17 | * <http://www.doctrine-project.org>. |
||
18 | */ |
||
19 | |||
20 | namespace Doctrine\ORM\Internal\Hydration; |
||
21 | |||
22 | use Doctrine\ORM\UnitOfWork; |
||
23 | use PDO; |
||
24 | use Doctrine\ORM\Mapping\ClassMetadata; |
||
25 | use Doctrine\ORM\PersistentCollection; |
||
26 | use Doctrine\ORM\Query; |
||
27 | use Doctrine\Common\Collections\ArrayCollection; |
||
28 | use Doctrine\ORM\Proxy\Proxy; |
||
29 | |||
30 | /** |
||
31 | * The ObjectHydrator constructs an object graph out of an SQL result set. |
||
32 | * |
||
33 | * Internal note: Highly performance-sensitive code. |
||
34 | * |
||
35 | * @since 2.0 |
||
36 | * @author Roman Borschel <[email protected]> |
||
37 | * @author Guilherme Blanco <[email protected]> |
||
38 | * @author Fabio B. Silva <[email protected]> |
||
39 | */ |
||
40 | class ObjectHydrator extends AbstractHydrator |
||
41 | { |
||
42 | /** |
||
43 | * @var array |
||
44 | */ |
||
45 | private $identifierMap = []; |
||
46 | |||
47 | /** |
||
48 | * @var array |
||
49 | */ |
||
50 | private $resultPointers = []; |
||
51 | |||
52 | /** |
||
53 | * @var array |
||
54 | */ |
||
55 | private $idTemplate = []; |
||
56 | |||
57 | /** |
||
58 | * @var integer |
||
59 | */ |
||
60 | private $resultCounter = 0; |
||
61 | |||
62 | /** |
||
63 | * @var array |
||
64 | */ |
||
65 | private $rootAliases = []; |
||
66 | |||
67 | /** |
||
68 | * @var array |
||
69 | */ |
||
70 | private $initializedCollections = []; |
||
71 | |||
72 | /** |
||
73 | * @var array |
||
74 | */ |
||
75 | private $existingCollections = []; |
||
76 | |||
77 | /** |
||
78 | * {@inheritdoc} |
||
79 | */ |
||
80 | 684 | protected function prepare() |
|
81 | { |
||
82 | 684 | if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) { |
|
83 | 583 | $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true; |
|
84 | } |
||
85 | |||
86 | 684 | foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { |
|
87 | 650 | $this->identifierMap[$dqlAlias] = []; |
|
88 | 650 | $this->idTemplate[$dqlAlias] = ''; |
|
89 | |||
90 | // Remember which associations are "fetch joined", so that we know where to inject |
||
91 | // collection stubs or proxies and where not. |
||
92 | 650 | if ( ! isset($this->_rsm->relationMap[$dqlAlias])) { |
|
93 | 650 | continue; |
|
94 | } |
||
95 | |||
96 | 329 | $parent = $this->_rsm->parentAliasMap[$dqlAlias]; |
|
97 | |||
98 | 329 | if ( ! isset($this->_rsm->aliasMap[$parent])) { |
|
99 | 1 | throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent); |
|
100 | } |
||
101 | |||
102 | 328 | $sourceClassName = $this->_rsm->aliasMap[$parent]; |
|
103 | 328 | $sourceClass = $this->getClassMetadata($sourceClassName); |
|
104 | 328 | $assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; |
|
105 | |||
106 | 328 | $this->_hints['fetched'][$parent][$assoc['fieldName']] = true; |
|
107 | |||
108 | 328 | if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) { |
|
109 | 35 | continue; |
|
110 | } |
||
111 | |||
112 | // Mark any non-collection opposite sides as fetched, too. |
||
113 | 307 | if ($assoc['mappedBy']) { |
|
114 | 258 | $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true; |
|
115 | |||
116 | 258 | continue; |
|
117 | } |
||
118 | |||
119 | // handle fetch-joined owning side bi-directional one-to-one associations |
||
120 | 68 | if ($assoc['inversedBy']) { |
|
121 | 47 | $class = $this->getClassMetadata($className); |
|
122 | 47 | $inverseAssoc = $class->associationMappings[$assoc['inversedBy']]; |
|
123 | |||
124 | 47 | if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) { |
|
125 | 28 | continue; |
|
126 | } |
||
127 | |||
128 | 41 | $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true; |
|
129 | } |
||
130 | } |
||
131 | 683 | } |
|
132 | |||
133 | /** |
||
134 | * {@inheritdoc} |
||
135 | */ |
||
136 | 678 | protected function cleanup() |
|
137 | { |
||
138 | 678 | $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true; |
|
139 | |||
140 | 678 | parent::cleanup(); |
|
141 | |||
142 | 678 | $this->identifierMap = |
|
143 | 678 | $this->initializedCollections = |
|
144 | 678 | $this->existingCollections = |
|
145 | 678 | $this->resultPointers = []; |
|
146 | |||
147 | 678 | if ($eagerLoad) { |
|
148 | 678 | $this->_uow->triggerEagerLoads(); |
|
149 | } |
||
150 | |||
151 | 678 | $this->_uow->hydrationComplete(); |
|
152 | 678 | } |
|
153 | |||
154 | /** |
||
155 | * {@inheritdoc} |
||
156 | */ |
||
157 | 677 | View Code Duplication | protected function hydrateAllData() |
158 | { |
||
159 | 677 | $result = []; |
|
160 | |||
161 | 677 | while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { |
|
162 | 638 | $this->hydrateRowData($row, $result); |
|
163 | } |
||
164 | |||
165 | // Take snapshots from all newly initialized collections |
||
166 | 674 | foreach ($this->initializedCollections as $coll) { |
|
167 | 71 | $coll->takeSnapshot(); |
|
168 | } |
||
169 | |||
170 | 674 | return $result; |
|
171 | } |
||
172 | |||
173 | /** |
||
174 | * Initializes a related collection. |
||
175 | * |
||
176 | * @param object $entity The entity to which the collection belongs. |
||
177 | * @param ClassMetadata $class |
||
178 | * @param string $fieldName The name of the field on the entity that holds the collection. |
||
179 | * @param string $parentDqlAlias Alias of the parent fetch joining this collection. |
||
180 | * |
||
181 | * @return \Doctrine\ORM\PersistentCollection |
||
182 | */ |
||
183 | 103 | private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias) |
|
184 | { |
||
185 | 103 | $oid = spl_object_hash($entity); |
|
186 | 103 | $relation = $class->associationMappings[$fieldName]; |
|
187 | 103 | $value = $class->reflFields[$fieldName]->getValue($entity); |
|
188 | |||
189 | 103 | if ($value === null || is_array($value)) { |
|
190 | 67 | $value = new ArrayCollection((array) $value); |
|
191 | } |
||
192 | |||
193 | 103 | if ( ! $value instanceof PersistentCollection) { |
|
194 | 67 | $value = new PersistentCollection( |
|
195 | 67 | $this->_em, $this->_metadataCache[$relation['targetEntity']], $value |
|
196 | ); |
||
197 | 67 | $value->setOwner($entity, $relation); |
|
198 | |||
199 | 67 | $class->reflFields[$fieldName]->setValue($entity, $value); |
|
200 | 67 | $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value); |
|
201 | |||
202 | 67 | $this->initializedCollections[$oid . $fieldName] = $value; |
|
203 | } else if ( |
||
204 | 40 | isset($this->_hints[Query::HINT_REFRESH]) || |
|
205 | 40 | isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) && |
|
206 | 40 | ! $value->isInitialized() |
|
207 | ) { |
||
208 | // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED! |
||
209 | 6 | $value->setDirty(false); |
|
210 | 6 | $value->setInitialized(true); |
|
211 | 6 | $value->unwrap()->clear(); |
|
212 | |||
213 | 6 | $this->initializedCollections[$oid . $fieldName] = $value; |
|
214 | } else { |
||
215 | // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN! |
||
216 | 35 | $this->existingCollections[$oid . $fieldName] = $value; |
|
217 | } |
||
218 | |||
219 | 103 | return $value; |
|
220 | } |
||
221 | |||
222 | /** |
||
223 | * Gets an entity instance. |
||
224 | * |
||
225 | * @param array $data The instance data. |
||
226 | * @param string $dqlAlias The DQL alias of the entity's class. |
||
227 | * |
||
228 | * @return object The entity. |
||
229 | * |
||
230 | * @throws HydrationException |
||
231 | */ |
||
232 | 608 | private function getEntity(array $data, $dqlAlias) |
|
233 | { |
||
234 | 608 | $className = $this->_rsm->aliasMap[$dqlAlias]; |
|
235 | |||
236 | 608 | if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) { |
|
237 | 69 | $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias]; |
|
238 | |||
239 | 69 | if ( ! isset($this->_rsm->metaMappings[$fieldName])) { |
|
240 | 1 | throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias); |
|
241 | } |
||
242 | |||
243 | 68 | $discrColumn = $this->_rsm->metaMappings[$fieldName]; |
|
244 | |||
245 | 68 | if ( ! isset($data[$discrColumn])) { |
|
246 | 1 | throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias); |
|
247 | } |
||
248 | |||
249 | 68 | if ($data[$discrColumn] === "") { |
|
250 | throw HydrationException::emptyDiscriminatorValue($dqlAlias); |
||
251 | } |
||
252 | |||
253 | 68 | $discrMap = $this->_metadataCache[$className]->discriminatorMap; |
|
254 | 68 | $discriminatorValue = (string) $data[$discrColumn]; |
|
255 | |||
256 | 68 | if ( ! isset($discrMap[$discriminatorValue])) { |
|
257 | 1 | throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap)); |
|
258 | } |
||
259 | |||
260 | 67 | $className = $discrMap[$discriminatorValue]; |
|
261 | |||
262 | 67 | unset($data[$discrColumn]); |
|
263 | } |
||
264 | |||
265 | 606 | if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) { |
|
266 | 24 | $this->registerManaged($this->_metadataCache[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data); |
|
267 | } |
||
268 | |||
269 | 606 | $this->_hints['fetchAlias'] = $dqlAlias; |
|
270 | |||
271 | 606 | return $this->_uow->createEntity($className, $data, $this->_hints); |
|
272 | } |
||
273 | |||
274 | /** |
||
275 | * @param string $className |
||
276 | * @param array $data |
||
277 | * |
||
278 | * @return mixed |
||
279 | */ |
||
280 | 35 | private function getEntityFromIdentityMap($className, array $data) |
|
281 | { |
||
282 | // TODO: Abstract this code and UnitOfWork::createEntity() equivalent? |
||
283 | 35 | $class = $this->_metadataCache[$className]; |
|
284 | |||
285 | /* @var $class ClassMetadata */ |
||
286 | 35 | if ($class->isIdentifierComposite) { |
|
287 | 1 | $idHash = ''; |
|
288 | |||
289 | 1 | View Code Duplication | foreach ($class->identifier as $fieldName) { |
290 | 1 | $idHash .= ' ' . (isset($class->associationMappings[$fieldName]) |
|
291 | 1 | ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] |
|
292 | 1 | : $data[$fieldName]); |
|
293 | } |
||
294 | |||
295 | 1 | return $this->_uow->tryGetByIdHash(ltrim($idHash), $class->rootEntityName); |
|
296 | 34 | } else if (isset($class->associationMappings[$class->identifier[0]])) { |
|
297 | return $this->_uow->tryGetByIdHash($data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']], $class->rootEntityName); |
||
298 | } |
||
299 | |||
300 | 34 | return $this->_uow->tryGetByIdHash($data[$class->identifier[0]], $class->rootEntityName); |
|
301 | } |
||
302 | |||
303 | /** |
||
304 | * Hydrates a single row in an SQL result set. |
||
305 | * |
||
306 | * @internal |
||
307 | * First, the data of the row is split into chunks where each chunk contains data |
||
308 | * that belongs to a particular component/class. Afterwards, all these chunks |
||
309 | * are processed, one after the other. For each chunk of class data only one of the |
||
310 | * following code paths is executed: |
||
311 | * |
||
312 | * Path A: The data chunk belongs to a joined/associated object and the association |
||
313 | * is collection-valued. |
||
314 | * Path B: The data chunk belongs to a joined/associated object and the association |
||
315 | * is single-valued. |
||
316 | * Path C: The data chunk belongs to a root result element/object that appears in the topmost |
||
317 | * level of the hydrated result. A typical example are the objects of the type |
||
318 | * specified by the FROM clause in a DQL query. |
||
319 | * |
||
320 | * @param array $row The data of the row to process. |
||
321 | * @param array $result The result array to fill. |
||
322 | * |
||
323 | * @return void |
||
324 | */ |
||
325 | 643 | protected function hydrateRowData(array $row, array &$result) |
|
326 | { |
||
327 | // Initialize |
||
328 | 643 | $id = $this->idTemplate; // initialize the id-memory |
|
329 | 643 | $nonemptyComponents = []; |
|
330 | // Split the row data into chunks of class data. |
||
331 | 643 | $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); |
|
332 | |||
333 | // reset result pointers for each data row |
||
334 | 643 | $this->resultPointers = []; |
|
335 | |||
336 | // Hydrate the data chunks |
||
337 | 643 | foreach ($rowData['data'] as $dqlAlias => $data) { |
|
338 | 608 | $entityName = $this->_rsm->aliasMap[$dqlAlias]; |
|
339 | |||
340 | 608 | if (isset($this->_rsm->parentAliasMap[$dqlAlias])) { |
|
341 | // It's a joined result |
||
342 | |||
343 | 320 | $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias]; |
|
344 | // we need the $path to save into the identifier map which entities were already |
||
345 | // seen for this parent-child relationship |
||
346 | 320 | $path = $parentAlias . '.' . $dqlAlias; |
|
347 | |||
348 | // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs |
||
349 | 320 | if ( ! isset($nonemptyComponents[$parentAlias])) { |
|
350 | // TODO: Add special case code where we hydrate the right join objects into identity map at least |
||
351 | 2 | continue; |
|
352 | } |
||
353 | |||
354 | 320 | $parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]]; |
|
355 | 320 | $relationField = $this->_rsm->relationMap[$dqlAlias]; |
|
356 | 320 | $relation = $parentClass->associationMappings[$relationField]; |
|
357 | 320 | $reflField = $parentClass->reflFields[$relationField]; |
|
358 | |||
359 | // Get a reference to the parent object to which the joined element belongs. |
||
360 | 320 | if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) { |
|
361 | 18 | $objectClass = $this->resultPointers[$parentAlias]; |
|
362 | 18 | $parentObject = $objectClass[key($objectClass)]; |
|
363 | 304 | } else if (isset($this->resultPointers[$parentAlias])) { |
|
364 | 304 | $parentObject = $this->resultPointers[$parentAlias]; |
|
365 | } else { |
||
366 | // Parent object of relation not found, mark as not-fetched again |
||
367 | 2 | $element = $this->getEntity($data, $dqlAlias); |
|
368 | |||
369 | // Update result pointer and provide initial fetch data for parent |
||
370 | 2 | $this->resultPointers[$dqlAlias] = $element; |
|
371 | 2 | $rowData['data'][$parentAlias][$relationField] = $element; |
|
372 | |||
373 | // Mark as not-fetched again |
||
374 | 2 | unset($this->_hints['fetched'][$parentAlias][$relationField]); |
|
375 | 2 | continue; |
|
376 | } |
||
377 | |||
378 | 320 | $oid = spl_object_hash($parentObject); |
|
379 | |||
380 | // Check the type of the relation (many or single-valued) |
||
381 | 320 | if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { |
|
382 | // PATH A: Collection-valued association |
||
383 | 104 | $reflFieldValue = $reflField->getValue($parentObject); |
|
384 | |||
385 | 104 | if (isset($nonemptyComponents[$dqlAlias])) { |
|
386 | 100 | $collKey = $oid . $relationField; |
|
387 | 100 | if (isset($this->initializedCollections[$collKey])) { |
|
388 | 47 | $reflFieldValue = $this->initializedCollections[$collKey]; |
|
389 | 100 | } else if ( ! isset($this->existingCollections[$collKey])) { |
|
390 | 100 | $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); |
|
391 | } |
||
392 | |||
393 | 100 | $indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]); |
|
394 | 100 | $index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false; |
|
395 | 100 | $indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false; |
|
396 | |||
397 | 100 | if ( ! $indexExists || ! $indexIsValid) { |
|
398 | 100 | if (isset($this->existingCollections[$collKey])) { |
|
399 | // Collection exists, only look for the element in the identity map. |
||
400 | 35 | if ($element = $this->getEntityFromIdentityMap($entityName, $data)) { |
|
401 | 35 | $this->resultPointers[$dqlAlias] = $element; |
|
402 | } else { |
||
403 | 35 | unset($this->resultPointers[$dqlAlias]); |
|
404 | } |
||
405 | } else { |
||
406 | 68 | $element = $this->getEntity($data, $dqlAlias); |
|
407 | |||
408 | 68 | if (isset($this->_rsm->indexByMap[$dqlAlias])) { |
|
409 | 12 | $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]]; |
|
410 | 12 | $reflFieldValue->hydrateSet($indexValue, $element); |
|
411 | 12 | $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue; |
|
412 | } else { |
||
413 | 56 | $reflFieldValue->hydrateAdd($element); |
|
414 | 56 | $reflFieldValue->last(); |
|
415 | 56 | $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key(); |
|
416 | } |
||
417 | // Update result pointer |
||
418 | 100 | $this->resultPointers[$dqlAlias] = $element; |
|
419 | } |
||
420 | } else { |
||
421 | // Update result pointer |
||
422 | 100 | $this->resultPointers[$dqlAlias] = $reflFieldValue[$index]; |
|
423 | } |
||
424 | 9 | } else if ( ! $reflFieldValue) { |
|
425 | 5 | $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias); |
|
426 | 6 | } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) { |
|
427 | 104 | $reflFieldValue->setInitialized(true); |
|
428 | } |
||
429 | |||
430 | } else { |
||
431 | // PATH B: Single-valued association |
||
432 | 238 | $reflFieldValue = $reflField->getValue($parentObject); |
|
433 | |||
434 | 238 | if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) { |
|
435 | // we only need to take action if this value is null, |
||
436 | // we refresh the entity or its an uninitialized proxy. |
||
437 | 223 | if (isset($nonemptyComponents[$dqlAlias])) { |
|
438 | 118 | $element = $this->getEntity($data, $dqlAlias); |
|
439 | 117 | $reflField->setValue($parentObject, $element); |
|
440 | 117 | $this->_uow->setOriginalEntityProperty($oid, $relationField, $element); |
|
441 | 117 | $targetClass = $this->_metadataCache[$relation['targetEntity']]; |
|
442 | |||
443 | 117 | if ($relation['isOwningSide']) { |
|
444 | // TODO: Just check hints['fetched'] here? |
||
445 | // If there is an inverse mapping on the target class its bidirectional |
||
446 | 53 | if ($relation['inversedBy']) { |
|
447 | 36 | $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']]; |
|
448 | 36 | View Code Duplication | if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) { |
0 ignored issues
–
show
|
|||
449 | 13 | $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject); |
|
450 | 36 | $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject); |
|
451 | } |
||
452 | 18 | } else if ($parentClass === $targetClass && $relation['mappedBy']) { |
|
453 | // Special case: bi-directional self-referencing one-one on the same class |
||
454 | 53 | $targetClass->reflFields[$relationField]->setValue($element, $parentObject); |
|
455 | } |
||
456 | View Code Duplication | } else { |
|
457 | // For sure bidirectional, as there is no inverse side in unidirectional mappings |
||
458 | 67 | $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject); |
|
459 | 67 | $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject); |
|
460 | } |
||
461 | // Update result pointer |
||
462 | 117 | $this->resultPointers[$dqlAlias] = $element; |
|
463 | } else { |
||
464 | 125 | $this->_uow->setOriginalEntityProperty($oid, $relationField, null); |
|
465 | 222 | $reflField->setValue($parentObject, null); |
|
466 | } |
||
467 | // else leave $reflFieldValue null for single-valued associations |
||
468 | } else { |
||
469 | // Update result pointer |
||
470 | 319 | $this->resultPointers[$dqlAlias] = $reflFieldValue; |
|
471 | } |
||
472 | } |
||
473 | } else { |
||
474 | // PATH C: Its a root result element |
||
475 | 608 | $this->rootAliases[$dqlAlias] = true; // Mark as root alias |
|
476 | 608 | $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0; |
|
477 | |||
478 | // if this row has a NULL value for the root result id then make it a null result. |
||
479 | 608 | View Code Duplication | if ( ! isset($nonemptyComponents[$dqlAlias]) ) { |
480 | 6 | if ($this->_rsm->isMixed) { |
|
481 | 2 | $result[] = [$entityKey => null]; |
|
482 | } else { |
||
483 | 4 | $result[] = null; |
|
484 | } |
||
485 | 6 | $resultKey = $this->resultCounter; |
|
486 | 6 | ++$this->resultCounter; |
|
487 | 6 | continue; |
|
488 | } |
||
489 | |||
490 | // check for existing result from the iterations before |
||
491 | 608 | if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) { |
|
492 | 608 | $element = $this->getEntity($data, $dqlAlias); |
|
493 | |||
494 | 606 | if ($this->_rsm->isMixed) { |
|
495 | 41 | $element = [$entityKey => $element]; |
|
496 | } |
||
497 | |||
498 | 606 | if (isset($this->_rsm->indexByMap[$dqlAlias])) { |
|
499 | 27 | $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]]; |
|
500 | |||
501 | 27 | if (isset($this->_hints['collection'])) { |
|
502 | 10 | $this->_hints['collection']->hydrateSet($resultKey, $element); |
|
503 | } |
||
504 | |||
505 | 27 | $result[$resultKey] = $element; |
|
506 | } else { |
||
507 | 589 | $resultKey = $this->resultCounter; |
|
508 | 589 | ++$this->resultCounter; |
|
509 | |||
510 | 589 | if (isset($this->_hints['collection'])) { |
|
511 | 110 | $this->_hints['collection']->hydrateAdd($element); |
|
512 | } |
||
513 | |||
514 | 589 | $result[] = $element; |
|
515 | } |
||
516 | |||
517 | 606 | $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey; |
|
518 | |||
519 | // Update result pointer |
||
520 | 606 | $this->resultPointers[$dqlAlias] = $element; |
|
521 | |||
522 | } else { |
||
523 | // Update result pointer |
||
524 | 78 | $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]]; |
|
525 | 78 | $this->resultPointers[$dqlAlias] = $result[$index]; |
|
526 | 78 | $resultKey = $index; |
|
527 | } |
||
528 | } |
||
529 | |||
530 | 606 | View Code Duplication | if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) { |
531 | 606 | $this->_uow->hydrationComplete(); |
|
532 | } |
||
533 | } |
||
534 | |||
535 | 640 | if ( ! isset($resultKey) ) { |
|
536 | 35 | $this->resultCounter++; |
|
537 | } |
||
538 | |||
539 | // Append scalar values to mixed result sets |
||
540 | 640 | View Code Duplication | if (isset($rowData['scalars'])) { |
541 | 54 | if ( ! isset($resultKey) ) { |
|
542 | 22 | $resultKey = (isset($this->_rsm->indexByMap['scalars'])) |
|
543 | 2 | ? $row[$this->_rsm->indexByMap['scalars']] |
|
544 | 22 | : $this->resultCounter - 1; |
|
545 | } |
||
546 | |||
547 | 54 | foreach ($rowData['scalars'] as $name => $value) { |
|
548 | 54 | $result[$resultKey][$name] = $value; |
|
549 | } |
||
550 | } |
||
551 | |||
552 | // Append new object to mixed result sets |
||
553 | 640 | View Code Duplication | if (isset($rowData['newObjects'])) { |
554 | 19 | if ( ! isset($resultKey) ) { |
|
555 | 13 | $resultKey = $this->resultCounter - 1; |
|
556 | } |
||
557 | |||
558 | |||
559 | 19 | $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0); |
|
560 | |||
561 | 19 | foreach ($rowData['newObjects'] as $objIndex => $newObject) { |
|
562 | 19 | $class = $newObject['class']; |
|
563 | 19 | $args = $newObject['args']; |
|
564 | 19 | $obj = $class->newInstanceArgs($args); |
|
565 | |||
566 | 19 | if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) { |
|
567 | 10 | $result[$resultKey] = $obj; |
|
568 | |||
569 | 10 | continue; |
|
570 | } |
||
571 | |||
572 | 9 | $result[$resultKey][$objIndex] = $obj; |
|
573 | } |
||
574 | } |
||
575 | 640 | } |
|
576 | |||
577 | /** |
||
578 | * When executed in a hydrate() loop we may have to clear internal state to |
||
579 | * decrease memory consumption. |
||
580 | * |
||
581 | * @param mixed $eventArgs |
||
582 | * |
||
583 | * @return void |
||
584 | */ |
||
585 | 4 | public function onClear($eventArgs) |
|
586 | { |
||
587 | 4 | parent::onClear($eventArgs); |
|
588 | |||
589 | 4 | $aliases = array_keys($this->identifierMap); |
|
590 | |||
591 | 4 | $this->identifierMap = array_fill_keys($aliases, []); |
|
592 | 4 | } |
|
593 | } |
||
594 |
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.