Failed Conditions
Pull Request — master (#6649)
by Marco
62:58
created

ObjectHydrator::buildEntityTryGet()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 14
cts 14
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 3
nop 1
crap 6
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\EntityManagerInterface;
23
use Doctrine\ORM\Internal\Hydration\Cache\LazyPropertyMap;
24
use Doctrine\ORM\UnitOfWork;
25
use PDO;
26
use Doctrine\ORM\Mapping\ClassMetadata;
27
use Doctrine\ORM\PersistentCollection;
28
use Doctrine\ORM\Query;
29
use Doctrine\Common\Collections\ArrayCollection;
30
use Doctrine\ORM\Proxy\Proxy;
31
32
/**
33
 * The ObjectHydrator constructs an object graph out of an SQL result set.
34
 *
35
 * Internal note: Highly performance-sensitive code.
36
 *
37
 * @since  2.0
38
 * @author Roman Borschel <[email protected]>
39
 * @author Guilherme Blanco <[email protected]>
40
 * @author Fabio B. Silva <[email protected]>
41
 */
42
class ObjectHydrator extends AbstractHydrator
43
{
44
    /**
45
     * @var array
46
     */
47
    private $identifierMap = [];
48
49
    /**
50
     * @var object[] indexed by oid
51
     */
52
    private $trackedWritableEntities = [];
53
54
    /**
55
     * @var array
56
     */
57
    private $resultPointers = [];
58
59
    /**
60
     * @var array
61
     */
62
    private $idTemplate = [];
63
64
    /**
65
     * @var integer
66
     */
67
    private $resultCounter = 0;
68
69
    /**
70
     * @var array
71
     */
72
    private $rootAliases = [];
73
74
    /**
75
     * @var array
76
     */
77
    private $initializedCollections = [];
78
79
    /**
80 684
     * @var array
81
     */
82 684
    private $existingCollections = [];
83 583
84
    /**
85
     * @var LazyPropertyMap|callable[]
86 684
     */
87 650
    private $entityTryGetCallbacksByEntityName;
88 650
89
    public function __construct(EntityManagerInterface $em)
90
    {
91
        parent::__construct($em);
92 650
93 650
        $this->entityTryGetCallbacksByEntityName = new LazyPropertyMap([$this, 'buildEntityTryGet']);
94
    }
95
96 329
    /**
97
     * {@inheritdoc}
98 329
     */
99 1
    protected function prepare()
100
    {
101
        if ( ! isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) {
102 328
            $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
103 328
        }
104 328
105
        foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
106 328
            $this->identifierMap[$dqlAlias] = [];
107
            $this->idTemplate[$dqlAlias]    = '';
108 328
109 35
            // Remember which associations are "fetch joined", so that we know where to inject
110
            // collection stubs or proxies and where not.
111
            if ( ! isset($this->_rsm->relationMap[$dqlAlias])) {
112
                continue;
113 307
            }
114 258
115
            $parent = $this->_rsm->parentAliasMap[$dqlAlias];
116 258
117
            if ( ! isset($this->_rsm->aliasMap[$parent])) {
118
                throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
119
            }
120 68
121 47
            /* @var $sourceClass ClassMetadata */
122 47
            $sourceClass = $this->_metadataCache->{$this->_rsm->aliasMap[$parent]};
123
            $assoc       = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
124 47
125 28
            $this->_hints['fetched'][$parent][$assoc['fieldName']] = true;
126
127
            if ($assoc['type'] === ClassMetadata::MANY_TO_MANY) {
128 41
                continue;
129
            }
130
131 683
            // Mark any non-collection opposite sides as fetched, too.
132
            if ($assoc['mappedBy']) {
133
                $this->_hints['fetched'][$dqlAlias][$assoc['mappedBy']] = true;
134
135
                continue;
136 678
            }
137
138 678
            // handle fetch-joined owning side bi-directional one-to-one associations
139
            if ($assoc['inversedBy']) {
140 678
                /* @var $class ClassMetadata */
141
                $class        = $this->_metadataCache->{$className};
142 678
                $inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
143 678
144 678
                if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
145 678
                    continue;
146
                }
147 678
148 678
                $this->_hints['fetched'][$dqlAlias][$inverseAssoc['fieldName']] = true;
149
            }
150
        }
151 678
    }
152 678
153
    /**
154
     * {@inheritdoc}
155
     */
156
    protected function cleanup()
157 677
    {
158
        $eagerLoad = (isset($this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD])) && $this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] == true;
159 677
160
        parent::cleanup();
161 677
162 638
        $this->identifierMap =
163
        $this->initializedCollections =
164
        $this->existingCollections =
165
        $this->resultPointers = [];
166 674
167 71
        if ($eagerLoad) {
168
            $this->_uow->triggerEagerLoads();
169
        }
170 674
171
        $this->_uow->hydrationComplete();
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 View Code Duplication
    protected function hydrateAllData()
178
    {
179
        $result = [];
180
181
        while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
182
            $this->hydrateRowData($row, $result);
183 103
        }
184
185 103
        // Take snapshots from all newly initialized collections
186 103
        foreach ($this->initializedCollections as $coll) {
187 103
            $coll->takeSnapshot();
188
        }
189 103
190 67
        return $result;
191
    }
192
193 103
    /**
194 67
     * Initializes a related collection.
195 67
     *
196
     * @param object        $entity         The entity to which the collection belongs.
197 67
     * @param ClassMetadata $class
198
     * @param string        $fieldName      The name of the field on the entity that holds the collection.
199 67
     * @param string        $parentDqlAlias Alias of the parent fetch joining this collection.
200 67
     *
201
     * @return \Doctrine\ORM\PersistentCollection
202 67
     */
203
    private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
204 40
    {
205 40
        $oid      = spl_object_hash($entity);
206 40
        $relation = $class->associationMappings[$fieldName];
207
        $value    = $class->reflFields[$fieldName]->getValue($entity);
208
209 6
        if ($value === null || is_array($value)) {
210 6
            $value = new ArrayCollection((array) $value);
211 6
        }
212
213 6
        if ( ! $value instanceof PersistentCollection) {
214
            $value = new PersistentCollection(
215
                $this->_em,
216 35
                $this->_metadataCache->{$relation['targetEntity']},
217
                $value
218
            );
219 103
            $value->setOwner($entity, $relation);
220
221
            $class->reflFields[$fieldName]->setValue($entity, $value);
222
            $this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
223
224
            $this->initializedCollections[$oid . $fieldName] = $value;
225
        } else if (
226
            isset($this->_hints[Query::HINT_REFRESH]) ||
227
            isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
228
             ! $value->isInitialized()
229
        ) {
230
            // Is already PersistentCollection, but either REFRESH or FETCH-JOIN and UNINITIALIZED!
231
            $value->setDirty(false);
232 608
            $value->setInitialized(true);
233
            $value->unwrap()->clear();
234 608
235
            $this->initializedCollections[$oid . $fieldName] = $value;
236 608
        } else {
237 69
            // Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
238
            $this->existingCollections[$oid . $fieldName] = $value;
239 69
        }
240 1
241
        return $value;
242
    }
243 68
244
    /**
245 68
     * Gets an entity instance.
246 1
     *
247
     * @param array  $data     The instance data.
248
     * @param string $dqlAlias The DQL alias of the entity's class.
249 68
     *
250
     * @return object The entity.
251
     *
252
     * @throws HydrationException
253 68
     */
254 68
    private function getEntity(array $data, $dqlAlias)
255
    {
256 68
        $className = $this->_rsm->aliasMap[$dqlAlias];
257 1
258
        if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
259
            $fieldName = $this->_rsm->discriminatorColumns[$dqlAlias];
260 67
261
            if ( ! isset($this->_rsm->metaMappings[$fieldName])) {
262 67
                throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias);
263
            }
264
265 606
            $discrColumn = $this->_rsm->metaMappings[$fieldName];
266 24
267
            if ( ! isset($data[$discrColumn])) {
268
                throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);
269 606
            }
270
271 606
            if ($data[$discrColumn] === "") {
272
                throw HydrationException::emptyDiscriminatorValue($dqlAlias);
273
            }
274
275
            /* @var $class ClassMetadata */
276
            $class    = $this->_metadataCache->{$className};
277
            $discrMap = $class->discriminatorMap;
278
            $discriminatorValue = (string) $data[$discrColumn];
279
280 35
            if ( ! isset($discrMap[$discriminatorValue])) {
281
                throw HydrationException::invalidDiscriminatorValue($discriminatorValue, array_keys($discrMap));
282
            }
283 35
284
            $className = $discrMap[$discriminatorValue];
285
286 35
            unset($data[$discrColumn]);
287 1
        }
288
289 1
        if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) {
290 1
            $this->registerManaged($this->_metadataCache->{$className}, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
291 1
        }
292 1
293
        $this->_hints['fetchAlias'] = $dqlAlias;
294
295 1
        $managedEntity = ($this->entityTryGetCallbacksByEntityName->{$className})($data);
296 34
297
        // If the entity is not existing in the UnitOfWork, then we
298
        // are the one creating it: let's track the object hash to
299
        // allow writing to it. Note that we discard `$managedEntity`
300 34
        // anyway, as we need to call `createEntity` with the data
301
        // in any case to allow refreshing proxy information, for
302
        // example
303
        if (! $managedEntity) {
304
            $entity = $this->_uow->createEntity($className, $data, $this->_hints);
305
306
            $this->trackedWritableEntities[\spl_object_hash($entity)] = true;
307
308
            return $entity;
309
        }
310
311
        $entity = $this->_uow->createEntity($className, $data, $this->_hints);
312
313
        // If the entity is not created by us, we can only consider
314
        // it to be created in here if it's a non-initialized proxy
315
        if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
316
            $this->trackedWritableEntities[\spl_object_hash($entity)] = true;
317
        }
318
319
        return $entity;
320
    }
321
322
    /**
323
     * @internal do not use this or you shall be punished with the wrath of the debugger
324
     * @private
325 643
     *
326
     * @param string $className
327
     *
328 643
     * @return callable
329 643
     */
330
    public function buildEntityTryGet(string $className) : callable
331 643
    {
332
        /* @var $metadata ClassMetadata */
333
        $metadata       = $this->_metadataCache->{$className};
334 643
        $rootEntityName = $metadata->rootEntityName;
335
        /* @var $idColumns string[] */
336
        $idColumns      = [];
337 643
338 608
        foreach ($metadata->identifier as $idFieldName) {
339
            $idColumns[] = isset($metadata->associationMappings[$idFieldName])
340 608
                ? $metadata->associationMappings[$idFieldName]['joinColumns'][0]['name']
341
                : $idFieldName;
342
        }
343 320
344
        /**
345
         * @return object|Proxy|null
346 320
         */
347
        return function (array $data) use ($idColumns, $rootEntityName) {
348
            $idHashData = [];
349 320
350
            foreach ($idColumns as $idColumn) {
351 2
                if (! isset($data[$idColumn])) {
352
                    return null;
353
                }
354 320
355 320
                $idHashData[] = $data[$idColumn];
356 320
            }
357 320
358
            return $this->_uow->tryGetByIdHash(\implode(' ', $idHashData), $rootEntityName) ?: null;
359
        };
360 320
    }
361 18
362 18
    /**
363 304
     * Hydrates a single row in an SQL result set.
364 304
     *
365
     * @internal
366
     * First, the data of the row is split into chunks where each chunk contains data
367 2
     * that belongs to a particular component/class. Afterwards, all these chunks
368
     * are processed, one after the other. For each chunk of class data only one of the
369
     * following code paths is executed:
370 2
     *
371 2
     * Path A: The data chunk belongs to a joined/associated object and the association
372
     *         is collection-valued.
373
     * Path B: The data chunk belongs to a joined/associated object and the association
374 2
     *         is single-valued.
375 2
     * Path C: The data chunk belongs to a root result element/object that appears in the topmost
376
     *         level of the hydrated result. A typical example are the objects of the type
377
     *         specified by the FROM clause in a DQL query.
378 320
     *
379
     * @param array $row    The data of the row to process.
380
     * @param array $result The result array to fill.
381 320
     *
382
     * @return void
383 104
     */
384
    protected function hydrateRowData(array $row, array &$result)
385 104
    {
386 100
        // Initialize
387 100
        $id = $this->idTemplate; // initialize the id-memory
388 47
        $nonemptyComponents = [];
389 100
        // Split the row data into chunks of class data.
390 100
        $rowData = $this->gatherRowData($row, $id, $nonemptyComponents);
391
392
        // reset result pointers for each data row
393 100
        $this->resultPointers = [];
394 100
395 100
        // Hydrate the data chunks
396
        foreach ($rowData['data'] as $dqlAlias => $data) {
397 100
            $entityName = $this->_rsm->aliasMap[$dqlAlias];
398 100
399
            if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
400 35
                // It's a joined result
401 35
402
                $parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
403 35
                // we need the $path to save into the identifier map which entities were already
404
                // seen for this parent-child relationship
405
                $path = $parentAlias . '.' . $dqlAlias;
406 68
407
                // We have a RIGHT JOIN result here. Doctrine cannot hydrate RIGHT JOIN Object-Graphs
408 68
                if ( ! isset($nonemptyComponents[$parentAlias])) {
409 12
                    // TODO: Add special case code where we hydrate the right join objects into identity map at least
410 12
                    continue;
411 12
                }
412
413 56
                $parentClass    = $this->_metadataCache->{$this->_rsm->aliasMap[$parentAlias]};
414 56
                $relationField  = $this->_rsm->relationMap[$dqlAlias];
415 56
                $relation       = $parentClass->associationMappings[$relationField];
416
                $reflField      = $parentClass->reflFields[$relationField];
417
418 100
                // Get a reference to the parent object to which the joined element belongs.
419
                if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
420
                    $objectClass = $this->resultPointers[$parentAlias];
421
                    $parentObject = $objectClass[key($objectClass)];
422 100
                } else if (isset($this->resultPointers[$parentAlias])) {
423
                    $parentObject = $this->resultPointers[$parentAlias];
424 9
                } else {
425 5
                    // Parent object of relation not found, mark as not-fetched again
426 6
                    $element = $this->getEntity($data, $dqlAlias);
427 104
428
                    // Update result pointer and provide initial fetch data for parent
429
                    $this->resultPointers[$dqlAlias] = $element;
430
                    $rowData['data'][$parentAlias][$relationField] = $element;
431
432 238
                    // Mark as not-fetched again
433
                    unset($this->_hints['fetched'][$parentAlias][$relationField]);
434 238
                    continue;
435
                }
436
437 223
                $oid = spl_object_hash($parentObject);
438 118
439 117
                // Check the type of the relation (many or single-valued)
440 117
                if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
441 117
                    // PATH A: Collection-valued association
442
                    $reflFieldValue = $reflField->getValue($parentObject);
443 117
444
                    if (isset($nonemptyComponents[$dqlAlias])) {
445
                        $collKey = $oid . $relationField;
446 53
                        if (isset($this->initializedCollections[$collKey])) {
447 36
                            $reflFieldValue = $this->initializedCollections[$collKey];
448 36
                        } else if ( ! isset($this->existingCollections[$collKey])) {
449 13
                            $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
450 36
                        }
451
452 18
                        $indexExists    = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
453
                        $index          = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
454 53
                        $indexIsValid   = $index !== false ? isset($reflFieldValue[$index]) : false;
455
456
                        if ( ! $indexExists || ! $indexIsValid) {
457
                            if (isset($this->existingCollections[$collKey])) {
458 67
                                // Collection exists, only look for the element in the identity map.
459 67
                                if ($element = ($this->entityTryGetCallbacksByEntityName->{$entityName})($data)) {
460
                                    $this->resultPointers[$dqlAlias] = $element;
461
                                } else {
462 117
                                    unset($this->resultPointers[$dqlAlias]);
463
                                }
464 125
                            } else {
465 222
                                $element = $this->getEntity($data, $dqlAlias);
466
467
                                if (isset($this->_rsm->indexByMap[$dqlAlias])) {
468
                                    $indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
469
                                    $reflFieldValue->hydrateSet($indexValue, $element);
470 319
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
471
                                } else {
472
                                    $reflFieldValue->hydrateAdd($element);
473
                                    $reflFieldValue->last();
474
                                    $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
475 608
                                }
476 608
                                // Update result pointer
477
                                $this->resultPointers[$dqlAlias] = $element;
478
                            }
479 608
                        } else {
480 6
                            // Update result pointer
481 2
                            $this->resultPointers[$dqlAlias] = $reflFieldValue[$index];
482
                        }
483 4
                    } else if ( ! $reflFieldValue) {
484
                        $reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
0 ignored issues
show
Unused Code introduced by
$reflFieldValue is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
485 6
                    } else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
486 6
                        $reflFieldValue->setInitialized(true);
487 6
                    }
488
489
                } else {
490
                    // PATH B: Single-valued association
491 608
                    $reflFieldValue = $reflField->getValue($parentObject);
492 608
493
                    if (isset($this->trackedWritableEntities[$oid]) || isset($this->_hints[Query::HINT_REFRESH])) {
494 606
                        // we only need to take action if `$parentObject` was not built or is not to be refreshed by this hydrator,
495 41
                        if (isset($nonemptyComponents[$dqlAlias])) {
496
                            $element = $this->getEntity($data, $dqlAlias);
497
                            $reflField->setValue($parentObject, $element);
498 606
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
499 27
                            $targetClass = $this->_metadataCache->{$relation['targetEntity']};
500
501 27
                            if ($relation['isOwningSide']) {
502 10
                                // TODO: Just check hints['fetched'] here?
503
                                // If there is an inverse mapping on the target class its bidirectional
504
                                if ($relation['inversedBy']) {
505 27
                                    $inverseAssoc = $targetClass->associationMappings[$relation['inversedBy']];
506 View Code Duplication
                                    if ($inverseAssoc['type'] & ClassMetadata::TO_ONE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
507 589
                                        $targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($element, $parentObject);
508 589
                                        $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $inverseAssoc['fieldName'], $parentObject);
509
                                    }
510 589
                                } else if ($parentClass === $targetClass && $relation['mappedBy']) {
511 110
                                    // Special case: bi-directional self-referencing one-one on the same class
512
                                    $targetClass->reflFields[$relationField]->setValue($element, $parentObject);
513
                                }
514 589 View Code Duplication
                            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
515
                                // For sure bidirectional, as there is no inverse side in unidirectional mappings
516
                                $targetClass->reflFields[$relation['mappedBy']]->setValue($element, $parentObject);
517 606
                                $this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
518
                            }
519
                            // Update result pointer
520 606
                            $this->resultPointers[$dqlAlias] = $element;
521
                        } else {
522
                            $this->_uow->setOriginalEntityProperty($oid, $relationField, null);
523
                            $reflField->setValue($parentObject, null);
524 78
                        }
525 78
                        // else leave $reflFieldValue null for single-valued associations
526 78
                    } else {
527
                        // Update result pointer
528
                        $this->resultPointers[$dqlAlias] = $reflFieldValue;
529
                    }
530 606
                }
531 606
            } else {
532
                // PATH C: Its a root result element
533
                $this->rootAliases[$dqlAlias] = true; // Mark as root alias
534
                $entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
535 640
536 35
                // if this row has a NULL value for the root result id then make it a null result.
537 View Code Duplication
                if ( ! isset($nonemptyComponents[$dqlAlias]) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
538
                    if ($this->_rsm->isMixed) {
539
                        $result[] = [$entityKey => null];
540 640
                    } else {
541 54
                        $result[] = null;
542 22
                    }
543 2
                    $resultKey = $this->resultCounter;
544 22
                    ++$this->resultCounter;
545
                    continue;
546
                }
547 54
548 54
                // check for existing result from the iterations before
549
                if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {
550
                    $element = $this->getEntity($data, $dqlAlias);
551
552
                    if ($this->_rsm->isMixed) {
553 640
                        $element = [$entityKey => $element];
554 19
                    }
555 13
556
                    if (isset($this->_rsm->indexByMap[$dqlAlias])) {
557
                        $resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
558
559 19
                        if (isset($this->_hints['collection'])) {
560
                            $this->_hints['collection']->hydrateSet($resultKey, $element);
561 19
                        }
562 19
563 19
                        $result[$resultKey] = $element;
564 19
                    } else {
565
                        $resultKey = $this->resultCounter;
566 19
                        ++$this->resultCounter;
567 10
568
                        if (isset($this->_hints['collection'])) {
569 10
                            $this->_hints['collection']->hydrateAdd($element);
570
                        }
571
572 9
                        $result[] = $element;
573
                    }
574
575 640
                    $this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
576
577
                    // Update result pointer
578
                    $this->resultPointers[$dqlAlias] = $element;
579
580
                } else {
581
                    // Update result pointer
582
                    $index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];
583
                    $this->resultPointers[$dqlAlias] = $result[$index];
584
                    $resultKey = $index;
585 4
                }
586
            }
587 4
588 View Code Duplication
            if (isset($this->_hints[Query::HINT_INTERNAL_ITERATION]) && $this->_hints[Query::HINT_INTERNAL_ITERATION]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
589 4
                $this->_uow->hydrationComplete();
590
            }
591 4
        }
592 4
593
        if ( ! isset($resultKey) ) {
594
            $this->resultCounter++;
595
        }
596
597
        // Append scalar values to mixed result sets
598 View Code Duplication
        if (isset($rowData['scalars'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
599
            if ( ! isset($resultKey) ) {
600
                $resultKey = (isset($this->_rsm->indexByMap['scalars']))
601
                    ? $row[$this->_rsm->indexByMap['scalars']]
602
                    : $this->resultCounter - 1;
603
            }
604
605
            foreach ($rowData['scalars'] as $name => $value) {
606
                $result[$resultKey][$name] = $value;
607
            }
608
        }
609
610
        // Append new object to mixed result sets
611 View Code Duplication
        if (isset($rowData['newObjects'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
612
            if ( ! isset($resultKey) ) {
613
                $resultKey = $this->resultCounter - 1;
614
            }
615
616
617
            $scalarCount = (isset($rowData['scalars'])? count($rowData['scalars']): 0);
618
619
            foreach ($rowData['newObjects'] as $objIndex => $newObject) {
620
                $class  = $newObject['class'];
621
                $args   = $newObject['args'];
622
                $obj    = $class->newInstanceArgs($args);
623
624
                if ($scalarCount == 0 && count($rowData['newObjects']) == 1 ) {
625
                    $result[$resultKey] = $obj;
626
627
                    continue;
628
                }
629
630
                $result[$resultKey][$objIndex] = $obj;
631
            }
632
        }
633
    }
634
635
    /**
636
     * When executed in a hydrate() loop we may have to clear internal state to
637
     * decrease memory consumption.
638
     *
639
     * @param mixed $eventArgs
640
     *
641
     * @return void
642
     */
643
    public function onClear($eventArgs)
644
    {
645
        parent::onClear($eventArgs);
646
647
        $aliases             = array_keys($this->identifierMap);
648
649
        $this->identifierMap = array_fill_keys($aliases, []);
650
    }
651
}
652