Failed Conditions
Pull Request — 2.7 (#8036)
by Benjamin
06:53
created

ClassMetadataFactory::initializeReflection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\Mapping;
21
22
use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory;
23
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
24
use Doctrine\Common\Persistence\Mapping\ReflectionService;
25
use Doctrine\DBAL\Platforms;
26
use Doctrine\ORM\EntityManagerInterface;
27
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
28
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
29
use Doctrine\ORM\Events;
30
use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
31
use Doctrine\ORM\Id\IdentityGenerator;
32
use Doctrine\ORM\ORMException;
33
use ReflectionException;
34
35
/**
36
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
37
 * metadata mapping information of a class which describes how a class should be mapped
38
 * to a relational database.
39
 *
40
 * @since   2.0
41
 * @author  Benjamin Eberlei <[email protected]>
42
 * @author  Guilherme Blanco <[email protected]>
43
 * @author  Jonathan Wage <[email protected]>
44
 * @author  Roman Borschel <[email protected]>
45
 */
46
class ClassMetadataFactory extends AbstractClassMetadataFactory
47
{
48
    /**
49
     * @var EntityManagerInterface|null
50
     */
51
    private $em;
52
53
    /**
54
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
55
     */
56
    private $targetPlatform;
57
58
    /**
59
     * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
60
     */
61
    private $driver;
62
63
    /**
64
     * @var \Doctrine\Common\EventManager
65
     */
66
    private $evm;
67
68
    /**
69
     * @var array
70
     */
71
    private $embeddablesActiveNesting = [];
72
73
    /**
74
     * {@inheritDoc}
75
     */
76 508
    protected function loadMetadata($name)
77
    {
78 508
        $loaded = parent::loadMetadata($name);
79
80 486
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
81
82 486
        return $loaded;
83
    }
84
85
    /**
86
     * @param EntityManagerInterface $em
87
     */
88 2508
    public function setEntityManager(EntityManagerInterface $em)
89
    {
90 2508
        $this->em = $em;
91 2508
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 578
    protected function initialize()
97
    {
98 578
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
0 ignored issues
show
Bug introduced by
The method getConfiguration() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

98
        $this->driver = $this->em->/** @scrutinizer ignore-call */ getConfiguration()->getMetadataDriverImpl();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
99 578
        $this->evm = $this->em->getEventManager();
100 578
        $this->initialized = true;
101 578
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 3
    protected function onNotFoundMetadata($className)
107
    {
108 3
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
109 1
            return;
110
        }
111
112 2
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can also be of type null; however, parameter $objectManager of Doctrine\ORM\Event\OnCla...ventArgs::__construct() does only seem to accept Doctrine\Common\Persistence\ObjectManager, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

112
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, /** @scrutinizer ignore-type */ $this->em);
Loading history...
113
114 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
115
116 2
        return $eventArgs->getFoundMetadata();
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122 505
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
123
    {
124
        /* @var $class ClassMetadata */
125
        /* @var $parent ClassMetadata */
126 505
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type Doctrine\ORM\Mapping\ClassMetadata, thus it always evaluated to true.
Loading history...
127 149
            $class->setInheritanceType($parent->inheritanceType);
128 149
            $class->setDiscriminatorColumn($parent->discriminatorColumn);
129 149
            $class->setIdGeneratorType($parent->generatorType);
130 149
            $this->addInheritedFields($class, $parent);
131 149
            $this->addInheritedRelations($class, $parent);
132 148
            $this->addInheritedEmbeddedClasses($class, $parent);
133 148
            $class->setIdentifier($parent->identifier);
134 148
            $class->setVersioned($parent->isVersioned);
135 148
            $class->setVersionField($parent->versionField);
136 148
            $class->setDiscriminatorMap($parent->discriminatorMap);
137 148
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
138 148
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
139
140 148
            if ( ! empty($parent->customGeneratorDefinition)) {
141 1
                $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
142
            }
143
144 148
            if ($parent->isMappedSuperclass) {
145 52
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
146
            }
147
        }
148
149
        // Invoke driver
150
        try {
151 505
            $this->driver->loadMetadataForClass($class->getName(), $class);
152 5
        } catch (ReflectionException $e) {
153
            throw MappingException::reflectionFailure($class->getName(), $e);
154
        }
155
156
        // If this class has a parent the id generator strategy is inherited.
157
        // However this is only true if the hierarchy of parents contains the root entity,
158
        // if it consists of mapped superclasses these don't necessarily include the id field.
159 500
        if ($parent && $rootEntityFound) {
160 100
            $this->inheritIdGeneratorMapping($class, $parent);
161
        } else {
162 498
            $this->completeIdGeneratorMapping($class);
163
        }
164
165 498
        if (!$class->isMappedSuperclass) {
166 496
            foreach ($class->embeddedClasses as $property => $embeddableClass) {
167
168 15
                if (isset($embeddableClass['inherited'])) {
169 2
                    continue;
170
                }
171
172 15
                if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) {
173 1
                    throw MappingException::missingEmbeddedClass($property);
174
                }
175
176 14
                if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
177 2
                    throw MappingException::infiniteEmbeddableNesting($class->name, $property);
178
                }
179
180 14
                $this->embeddablesActiveNesting[$class->name] = true;
181
182 14
                $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
183
184 13
                if ($embeddableMetadata->isEmbeddedClass) {
0 ignored issues
show
Bug introduced by
Accessing isEmbeddedClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
185 13
                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
186
                }
187
188 13
                $identifier = $embeddableMetadata->getIdentifier();
189
190 13
                if (! empty($identifier)) {
191 5
                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);
192
                }
193
194 13
                $class->inlineEmbeddable($property, $embeddableMetadata);
195
196 13
                unset($this->embeddablesActiveNesting[$class->name]);
197
            }
198
        }
199
200 495
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type Doctrine\ORM\Mapping\ClassMetadata, thus it always evaluated to true.
Loading history...
201 148
            if ($parent->isInheritanceTypeSingleTable()) {
202 38
                $class->setPrimaryTable($parent->table);
203
            }
204
205 148
            if ($parent) {
0 ignored issues
show
introduced by
$parent is of type Doctrine\ORM\Mapping\ClassMetadata, thus it always evaluated to true.
Loading history...
206 148
                $this->addInheritedIndexes($class, $parent);
207
            }
208
209 148
            if ($parent->cache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parent->cache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
210 3
                $class->cache = $parent->cache;
211
            }
212
213 148
            if ($parent->containsForeignIdentifier) {
214 1
                $class->containsForeignIdentifier = true;
215
            }
216
217 148
            if ( ! empty($parent->namedQueries)) {
218 1
                $this->addInheritedNamedQueries($class, $parent);
219
            }
220
221 148
            if ( ! empty($parent->namedNativeQueries)) {
222 9
                $this->addInheritedNamedNativeQueries($class, $parent);
223
            }
224
225 148
            if ( ! empty($parent->sqlResultSetMappings)) {
226 9
                $this->addInheritedSqlResultSetMappings($class, $parent);
227
            }
228
229 148
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
230 15
                $class->entityListeners = $parent->entityListeners;
231
            }
232
        }
233
234 495
        $class->setParentClasses($nonSuperclassParents);
235
236 495
        if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
237
            $this->addDefaultDiscriminatorMap($class);
238
        }
239
240 495
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
241 6
            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
242 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
243
        }
244
245 494
        $this->validateRuntimeMetadata($class, $parent);
246 493
    }
247
248
    /**
249
     * Validate runtime metadata is correctly defined.
250
     *
251
     * @param ClassMetadata               $class
252
     * @param ClassMetadataInterface|null $parent
253
     *
254
     * @return void
255
     *
256
     * @throws MappingException
257
     */
258 494
    protected function validateRuntimeMetadata($class, $parent)
259
    {
260 494
        if ( ! $class->reflClass ) {
261
            // only validate if there is a reflection class instance
262 8
            return;
263
        }
264
265 486
        $class->validateIdentifier();
266 486
        $class->validateAssociations();
267 486
        $class->validateLifecycleCallbacks($this->getReflectionService());
268
269
        // verify inheritance
270 486
        if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
271 90
            if ( ! $parent) {
272 87
                if (count($class->discriminatorMap) == 0) {
273
                    throw MappingException::missingDiscriminatorMap($class->name);
274
                }
275 87
                if ( ! $class->discriminatorColumn) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorColumn of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
276 90
                    throw MappingException::missingDiscriminatorColumn($class->name);
277
                }
278
            }
279 438
        } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorColumn of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
280
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
281 1
            throw MappingException::noInheritanceOnMappedSuperClass($class->name);
282
        }
283 485
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288 500
    protected function newClassMetadataInstance($className)
289
    {
290 500
        return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
291
    }
292
293
    /**
294
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
295
     * map classes and looking for a fitting one.
296
     *
297
     * @param ClassMetadata $metadata
298
     *
299
     * @return void
300
     *
301
     * @throws MappingException
302
     */
303 486
    private function resolveDiscriminatorValue(ClassMetadata $metadata)
304
    {
305 486
        if ($metadata->discriminatorValue
306 467
            || ! $metadata->discriminatorMap
307 52
            || $metadata->isMappedSuperclass
308 51
            || ! $metadata->reflClass
309 486
            || $metadata->reflClass->isAbstract()
310
        ) {
311 486
            return;
312
        }
313
314
        // minor optimization: avoid loading related metadata when not needed
315 5
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
316 5
            if ($discriminatorClass === $metadata->name) {
317 3
                $metadata->discriminatorValue = $discriminatorValue;
318
319 5
                return;
320
            }
321
        }
322
323
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
324 2
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
325 2
            if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) {
326 1
                $metadata->discriminatorValue = $discriminatorValue;
327
328 2
                return;
329
            }
330
        }
331
332 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName);
333
    }
334
335
    /**
336
     * Adds a default discriminator map if no one is given
337
     *
338
     * If an entity is of any inheritance type and does not contain a
339
     * discriminator map, then the map is generated automatically. This process
340
     * is expensive computation wise.
341
     *
342
     * The automatically generated discriminator map contains the lowercase short name of
343
     * each class as key.
344
     *
345
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
346
     *
347
     * @throws MappingException
348
     */
349
    private function addDefaultDiscriminatorMap(ClassMetadata $class)
350
    {
351
        $allClasses = $this->driver->getAllClassNames();
352
        $fqcn = $class->getName();
353
        $map = [$this->getShortName($class->name) => $fqcn];
354
355
        $duplicates = [];
356
        foreach ($allClasses as $subClassCandidate) {
357
            if (is_subclass_of($subClassCandidate, $fqcn)) {
358
                $shortName = $this->getShortName($subClassCandidate);
359
360
                if (isset($map[$shortName])) {
361
                    $duplicates[] = $shortName;
362
                }
363
364
                $map[$shortName] = $subClassCandidate;
365
            }
366
        }
367
368
        if ($duplicates) {
369
            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
370
        }
371
372
        $class->setDiscriminatorMap($map);
373
    }
374
375
    /**
376
     * Gets the lower-case short name of a class.
377
     *
378
     * @param string $className
379
     *
380
     * @return string
381
     */
382
    private function getShortName($className)
383
    {
384
        if (strpos($className, "\\") === false) {
385
            return strtolower($className);
386
        }
387
388
        $parts = explode("\\", $className);
389
390
        return strtolower(end($parts));
391
    }
392
393
    /**
394
     * Adds inherited fields to the subclass mapping.
395
     *
396
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
397
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
398
     *
399
     * @return void
400
     */
401 149
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
402
    {
403 149
        foreach ($parentClass->fieldMappings as $mapping) {
404 133
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass && ! $parentClass->isEmbeddedClass) {
405 100
                $mapping['inherited'] = $parentClass->name;
406
            }
407 133
            if (! isset($mapping['declared'])) {
408 133
                $mapping['declared'] = $parentClass->name;
409
            }
410 133
            $subClass->addInheritedFieldMapping($mapping);
411
        }
412 149
        foreach ($parentClass->reflFields as $name => $field) {
413 134
            $subClass->reflFields[$name] = $field;
414
        }
415 149
    }
416
417
    /**
418
     * Adds inherited association mappings to the subclass mapping.
419
     *
420
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
421
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
422
     *
423
     * @return void
424
     *
425
     * @throws MappingException
426
     */
427 149
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
428
    {
429 149
        foreach ($parentClass->associationMappings as $field => $mapping) {
430 59
            if ($parentClass->isMappedSuperclass) {
431 16
                if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
432 1
                    throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field);
433
                }
434 15
                $mapping['sourceEntity'] = $subClass->name;
435
            }
436
437
            //$subclassMapping = $mapping;
438 58
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
439 43
                $mapping['inherited'] = $parentClass->name;
440
            }
441 58
            if ( ! isset($mapping['declared'])) {
442 58
                $mapping['declared'] = $parentClass->name;
443
            }
444 58
            $subClass->addInheritedAssociationMapping($mapping);
445
        }
446 148
    }
447
448 148
    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
449
    {
450 148
        foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
451 3
            if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
452 2
                $embeddedClass['inherited'] = $parentClass->name;
453
            }
454 3
            if ( ! isset($embeddedClass['declared'])) {
455 3
                $embeddedClass['declared'] = $parentClass->name;
456
            }
457
458 3
            $subClass->embeddedClasses[$field] = $embeddedClass;
459
        }
460 148
    }
461
462
    /**
463
     * Adds nested embedded classes metadata to a parent class.
464
     *
465
     * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.
466
     * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
467
     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
468
     */
469 13
    private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
470
    {
471 13
        foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
472 5
            $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
473
474 5
            $parentClass->mapEmbedded(
475
                [
476 5
                    'fieldName' => $prefix . '.' . $property,
477 5
                    'class' => $embeddableMetadata->name,
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
478 5
                    'columnPrefix' => $embeddableClass['columnPrefix'],
479 5
                    'declaredField' => $embeddableClass['declaredField']
480 1
                            ? $prefix . '.' . $embeddableClass['declaredField']
481 5
                            : $prefix,
482 5
                    'originalField' => $embeddableClass['originalField'] ?: $property,
483
                ]
484
            );
485
        }
486 13
    }
487
488
    /**
489
     * Copy the table indices from the parent class superclass to the child class
490
     *
491
     * @param ClassMetadata $subClass
492
     * @param ClassMetadata $parentClass
493
     *
494
     * @return void
495
     */
496 148
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
497
    {
498 148
        if (! $parentClass->isMappedSuperclass) {
499 101
            return;
500
        }
501
502 52
        foreach (['uniqueConstraints', 'indexes'] as $indexType) {
503 52
            if (isset($parentClass->table[$indexType])) {
504 1
                foreach ($parentClass->table[$indexType] as $indexName => $index) {
505 1
                    if (isset($subClass->table[$indexType][$indexName])) {
506
                        continue; // Let the inheriting table override indices
507
                    }
508
509 52
                    $subClass->table[$indexType][$indexName] = $index;
510
                }
511
            }
512
        }
513 52
    }
514
515
    /**
516
     * Adds inherited named queries to the subclass mapping.
517
     *
518
     * @since 2.2
519
     *
520
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
521
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
522
     *
523
     * @return void
524
     */
525 1
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
526
    {
527 1
        foreach ($parentClass->namedQueries as $name => $query) {
528 1
            if ( ! isset ($subClass->namedQueries[$name])) {
529 1
                $subClass->addNamedQuery(
530
                    [
531 1
                        'name'  => $query['name'],
532 1
                        'query' => $query['query']
533
                    ]
534
                );
535
            }
536
        }
537 1
    }
538
539
    /**
540
     * Adds inherited named native queries to the subclass mapping.
541
     *
542
     * @since 2.3
543
     *
544
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
545
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
546
     *
547
     * @return void
548
     */
549 9
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
550
    {
551 9
        foreach ($parentClass->namedNativeQueries as $name => $query) {
552 9
            if ( ! isset ($subClass->namedNativeQueries[$name])) {
553 9
                $subClass->addNamedNativeQuery(
554
                    [
555 9
                        'name'              => $query['name'],
556 9
                        'query'             => $query['query'],
557 9
                        'isSelfClass'       => $query['isSelfClass'],
558 9
                        'resultSetMapping'  => $query['resultSetMapping'],
559 9
                        'resultClass'       => $query['isSelfClass'] ? $subClass->name : $query['resultClass'],
560
                    ]
561
                );
562
            }
563
        }
564 9
    }
565
566
    /**
567
     * Adds inherited sql result set mappings to the subclass mapping.
568
     *
569
     * @since 2.3
570
     *
571
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
572
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
573
     *
574
     * @return void
575
     */
576 9
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
577
    {
578 9
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
579 9
            if ( ! isset ($subClass->sqlResultSetMappings[$name])) {
580 9
                $entities = [];
581 9
                foreach ($mapping['entities'] as $entity) {
582 9
                    $entities[] = [
583 9
                        'fields'                => $entity['fields'],
584 9
                        'isSelfClass'           => $entity['isSelfClass'],
585 9
                        'discriminatorColumn'   => $entity['discriminatorColumn'],
586 9
                        'entityClass'           => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'],
587
                    ];
588
                }
589
590 9
                $subClass->addSqlResultSetMapping(
591
                    [
592 9
                        'name'          => $mapping['name'],
593 9
                        'columns'       => $mapping['columns'],
594 9
                        'entities'      => $entities,
595
                    ]
596
                );
597
            }
598
        }
599 9
    }
600
601
    /**
602
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
603
     * most appropriate for the targeted database platform.
604
     *
605
     * @param ClassMetadataInfo $class
606
     *
607
     * @return void
608
     *
609
     * @throws ORMException
610
     */
611 498
    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
612
    {
613 498
        $idGenType = $class->generatorType;
614 498
        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
615 354
            if ($this->getTargetPlatform()->prefersSequences()) {
616
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
617 354
            } else if ($this->getTargetPlatform()->prefersIdentityColumns()) {
618 354
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
619
            } else {
620
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
621
            }
622
        }
623
624
        // Create & assign an appropriate ID generator instance
625 498
        switch ($class->generatorType) {
626 498
            case ClassMetadata::GENERATOR_TYPE_IDENTITY:
627 367
                $sequenceName = null;
628 367
                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
629
630
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
631 367
                if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) {
632
                    $columnName     = $class->getSingleIdentifierColumnName();
633
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
634
                    $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform());
635
                    $sequenceName   = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName);
636
                    $definition     = [
637
                        'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName)
638
                    ];
639
640
                    if ($quoted) {
641
                        $definition['quoted'] = true;
642
                    }
643
644
                    $sequenceName = $this
645
                        ->em
646
                        ->getConfiguration()
647
                        ->getQuoteStrategy()
648
                        ->getSequenceName($definition, $class, $this->getTargetPlatform());
649
                }
650
651 367
                $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint')
652 1
                    ? new BigIntegerIdentityGenerator($sequenceName)
653 367
                    : new IdentityGenerator($sequenceName);
654
655 367
                $class->setIdGenerator($generator);
656
657 367
                break;
658
659 179
            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
660
                // If there is no sequence definition yet, create a default definition
661 6
                $definition = $class->sequenceGeneratorDefinition;
662
663 6
                if ( ! $definition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definition of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
664
                    $fieldName      = $class->getSingleIdentifierFieldName();
665
                    $sequenceName   = $class->getSequenceName($this->getTargetPlatform());
666
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
667
668
                    $definition = [
669
                        'sequenceName'      => $this->getTargetPlatform()->fixSchemaElementName($sequenceName),
670
                        'allocationSize'    => 1,
671
                        'initialValue'      => 1,
672
                    ];
673
674
                    if ($quoted) {
675
                        $definition['quoted'] = true;
676
                    }
677
678
                    $class->setSequenceGeneratorDefinition($definition);
679
                }
680
681 6
                $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
682 6
                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()),
683 6
                    $definition['allocationSize']
684
                );
685 6
                $class->setIdGenerator($sequenceGenerator);
686 6
                break;
687
688 176
            case ClassMetadata::GENERATOR_TYPE_NONE:
689 171
                $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
690 171
                break;
691
692 7
            case ClassMetadata::GENERATOR_TYPE_UUID:
693 2
                $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator());
694 2
                break;
695
696 5
            case ClassMetadata::GENERATOR_TYPE_TABLE:
697
                throw new ORMException("TableGenerator not yet implemented.");
698
                break;
699
700 5
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
701 5
                $definition = $class->customGeneratorDefinition;
702 5
                if ($definition === null) {
703 1
                    throw new ORMException("Can't instantiate custom generator : no custom generator definition");
704
                }
705 4
                if ( ! class_exists($definition['class'])) {
706 1
                    throw new ORMException("Can't instantiate custom generator : " .
707 1
                        $definition['class']);
708
                }
709 3
                $class->setIdGenerator(new $definition['class']);
710 3
                break;
711
712
            default:
713
                throw new ORMException("Unknown generator type: " . $class->generatorType);
714
        }
715 496
    }
716
717
    /**
718
     * Inherits the ID generator mapping from a parent class.
719
     *
720
     * @param ClassMetadataInfo $class
721
     * @param ClassMetadataInfo $parent
722
     */
723 105
    private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent)
724
    {
725 105
        if ($parent->isIdGeneratorSequence()) {
726 3
            $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
727 102
        } elseif ($parent->isIdGeneratorTable()) {
728
            $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition;
729
        }
730
731 105
        if ($parent->generatorType) {
732 105
            $class->setIdGeneratorType($parent->generatorType);
733
        }
734
735 105
        if ($parent->idGenerator) {
736 105
            $class->setIdGenerator($parent->idGenerator);
737
        }
738 105
    }
739
740
    /**
741
     * {@inheritDoc}
742
     */
743 2146
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
744
    {
745
        /* @var $class ClassMetadata */
746 2146
        $class->wakeupReflection($reflService);
747 2146
    }
748
749
    /**
750
     * {@inheritDoc}
751
     */
752 505
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
753
    {
754
        /* @var $class ClassMetadata */
755 505
        $class->initializeReflection($reflService);
756 505
    }
757
758
    /**
759
     * {@inheritDoc}
760
     */
761 4
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
762
    {
763 4
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
764
    }
765
766
    /**
767
     * {@inheritDoc}
768
     */
769 260
    protected function getDriver()
770
    {
771 260
        return $this->driver;
772
    }
773
774
    /**
775
     * {@inheritDoc}
776
     */
777 493
    protected function isEntity(ClassMetadataInterface $class)
778
    {
779 493
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false
0 ignored issues
show
Bug introduced by
Accessing isMappedSuperclass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
780 493
            && isset($class->isEmbeddedClass) && $class->isEmbeddedClass === false;
0 ignored issues
show
Bug introduced by
Accessing isEmbeddedClass on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
781
    }
782
783
    /**
784
     * @return Platforms\AbstractPlatform
785
     */
786 373
    private function getTargetPlatform()
787
    {
788 373
        if (!$this->targetPlatform) {
789 373
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
790
        }
791
792 373
        return $this->targetPlatform;
793
    }
794
}
795