Failed Conditions
Pull Request — 2.7 (#8138)
by Benjamin
07:31
created

ClassMetadataFactory::completeIdGeneratorMapping()   F

Complexity

Conditions 21
Paths 208

Size

Total Lines 103
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 44.2558

Importance

Changes 0
Metric Value
cc 21
eloc 69
c 0
b 0
f 0
nc 208
nop 1
dl 0
loc 103
ccs 40
cts 64
cp 0.625
crap 44.2558
rs 3.2333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\DBAL\Platforms;
23
use Doctrine\ORM\EntityManagerInterface;
24
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
25
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
26
use Doctrine\ORM\Events;
27
use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
28
use Doctrine\ORM\Id\IdentityGenerator;
29
use Doctrine\ORM\ORMException;
30
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
31
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
32
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
33
use Doctrine\Persistence\Mapping\ReflectionService;
34
use ReflectionException;
35
use function assert;
36
use function interface_exists;
37
38
/**
39
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
40
 * metadata mapping information of a class which describes how a class should be mapped
41
 * to a relational database.
42
 *
43
 * @since   2.0
44
 * @author  Benjamin Eberlei <[email protected]>
45
 * @author  Guilherme Blanco <[email protected]>
46
 * @author  Jonathan Wage <[email protected]>
47
 * @author  Roman Borschel <[email protected]>
48
 */
49
class ClassMetadataFactory extends AbstractClassMetadataFactory
50
{
51
    /**
52
     * @var EntityManagerInterface|null
53
     */
54
    private $em;
55
56
    /**
57
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
58
     */
59
    private $targetPlatform;
60
61
    /**
62
     * @var MappingDriver
63
     */
64
    private $driver;
65
66
    /**
67
     * @var \Doctrine\Common\EventManager
68
     */
69
    private $evm;
70
71
    /**
72
     * @var array
73
     */
74
    private $embeddablesActiveNesting = [];
75
76
    /**
77
     * {@inheritDoc}
78
     */
79 509
    protected function loadMetadata($name)
80
    {
81 509
        $loaded = parent::loadMetadata($name);
82
83 487
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
84
85 487
        return $loaded;
86
    }
87
88
    /**
89
     * @param EntityManagerInterface $em
90
     */
91 2509
    public function setEntityManager(EntityManagerInterface $em)
92
    {
93 2509
        $this->em = $em;
94 2509
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 579
    protected function initialize()
100
    {
101 579
        $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

101
        $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...
102 579
        $this->evm = $this->em->getEventManager();
103 579
        $this->initialized = true;
104 579
    }
105
106
    /**
107
     * {@inheritDoc}
108
     */
109 3
    protected function onNotFoundMetadata($className)
110
    {
111 3
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
112 1
            return;
113
        }
114
115 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\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

115
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, /** @scrutinizer ignore-type */ $this->em);
Loading history...
116
117 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
118
119 2
        return $eventArgs->getFoundMetadata();
120
    }
121
122
    /**
123
     * {@inheritDoc}
124
     */
125 506
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
126
    {
127
        /* @var $class ClassMetadata */
128
        /* @var $parent ClassMetadata */
129 506
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type Doctrine\ORM\Mapping\ClassMetadata, thus it always evaluated to true.
Loading history...
130 149
            $class->setInheritanceType($parent->inheritanceType);
131 149
            $class->setDiscriminatorColumn($parent->discriminatorColumn);
132 149
            $class->setIdGeneratorType($parent->generatorType);
133 149
            $this->addInheritedFields($class, $parent);
134 149
            $this->addInheritedRelations($class, $parent);
135 148
            $this->addInheritedEmbeddedClasses($class, $parent);
136 148
            $class->setIdentifier($parent->identifier);
137 148
            $class->setVersioned($parent->isVersioned);
138 148
            $class->setVersionField($parent->versionField);
139 148
            $class->setDiscriminatorMap($parent->discriminatorMap);
140 148
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
141 148
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
142
143 148
            if ( ! empty($parent->customGeneratorDefinition)) {
144 1
                $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
145
            }
146
147 148
            if ($parent->isMappedSuperclass) {
148 52
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
149
            }
150
        }
151
152
        // Invoke driver
153
        try {
154 506
            $this->driver->loadMetadataForClass($class->getName(), $class);
155 5
        } catch (ReflectionException $e) {
156
            throw MappingException::reflectionFailure($class->getName(), $e);
157
        }
158
159
        // If this class has a parent the id generator strategy is inherited.
160
        // However this is only true if the hierarchy of parents contains the root entity,
161
        // if it consists of mapped superclasses these don't necessarily include the id field.
162 501
        if ($parent && $rootEntityFound) {
163 101
            $this->inheritIdGeneratorMapping($class, $parent);
164
        } else {
165 499
            $this->completeIdGeneratorMapping($class);
166
        }
167
168 499
        if (!$class->isMappedSuperclass) {
169 497
            foreach ($class->embeddedClasses as $property => $embeddableClass) {
170
171 14
                if (isset($embeddableClass['inherited'])) {
172 1
                    continue;
173
                }
174
175 14
                if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) {
176 1
                    throw MappingException::missingEmbeddedClass($property);
177
                }
178
179 13
                if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
180 2
                    throw MappingException::infiniteEmbeddableNesting($class->name, $property);
181
                }
182
183 13
                $this->embeddablesActiveNesting[$class->name] = true;
184
185 13
                $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
186
187 12
                if ($embeddableMetadata->isEmbeddedClass) {
0 ignored issues
show
Bug introduced by
Accessing isEmbeddedClass on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
188 12
                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
189
                }
190
191 12
                $identifier = $embeddableMetadata->getIdentifier();
192
193 12
                if (! empty($identifier)) {
194 5
                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);
195
                }
196
197 12
                $class->inlineEmbeddable($property, $embeddableMetadata);
198
199 12
                unset($this->embeddablesActiveNesting[$class->name]);
200
            }
201
        }
202
203 496
        if ($parent) {
0 ignored issues
show
introduced by
$parent is of type Doctrine\ORM\Mapping\ClassMetadata, thus it always evaluated to true.
Loading history...
204 148
            if ($parent->isInheritanceTypeSingleTable()) {
205 38
                $class->setPrimaryTable($parent->table);
206
            }
207
208 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...
209 148
                $this->addInheritedIndexes($class, $parent);
210
            }
211
212 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...
213 3
                $class->cache = $parent->cache;
214
            }
215
216 148
            if ($parent->containsForeignIdentifier) {
217 1
                $class->containsForeignIdentifier = true;
218
            }
219
220 148
            if ( ! empty($parent->namedQueries)) {
221 1
                $this->addInheritedNamedQueries($class, $parent);
222
            }
223
224 148
            if ( ! empty($parent->namedNativeQueries)) {
225 9
                $this->addInheritedNamedNativeQueries($class, $parent);
226
            }
227
228 148
            if ( ! empty($parent->sqlResultSetMappings)) {
229 9
                $this->addInheritedSqlResultSetMappings($class, $parent);
230
            }
231
232 148
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
233 15
                $class->entityListeners = $parent->entityListeners;
234
            }
235
        }
236
237 496
        $class->setParentClasses($nonSuperclassParents);
238
239 496
        if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
240
            $this->addDefaultDiscriminatorMap($class);
241
        }
242
243 496
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
244 6
            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
245 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
246
        }
247
248 495
        $this->validateRuntimeMetadata($class, $parent);
249 494
    }
250
251
    /**
252
     * Validate runtime metadata is correctly defined.
253
     *
254
     * @param ClassMetadata               $class
255
     * @param ClassMetadataInterface|null $parent
256
     *
257
     * @return void
258
     *
259
     * @throws MappingException
260
     */
261 495
    protected function validateRuntimeMetadata($class, $parent)
262
    {
263 495
        if ( ! $class->reflClass ) {
264
            // only validate if there is a reflection class instance
265 8
            return;
266
        }
267
268 487
        $class->validateIdentifier();
269 487
        $class->validateAssociations();
270 487
        $class->validateLifecycleCallbacks($this->getReflectionService());
271
272
        // verify inheritance
273 487
        if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
274 91
            if ( ! $parent) {
275 88
                if (count($class->discriminatorMap) == 0) {
276
                    throw MappingException::missingDiscriminatorMap($class->name);
277
                }
278 88
                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...
279 91
                    throw MappingException::missingDiscriminatorColumn($class->name);
280
                }
281
            }
282 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...
283
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
284 1
            throw MappingException::noInheritanceOnMappedSuperClass($class->name);
285
        }
286 486
    }
287
288
    /**
289
     * {@inheritDoc}
290
     */
291 501
    protected function newClassMetadataInstance($className)
292
    {
293 501
        return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
294
    }
295
296
    /**
297
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
298
     * map classes and looking for a fitting one.
299
     *
300
     * @param ClassMetadata $metadata
301
     *
302
     * @return void
303
     *
304
     * @throws MappingException
305
     */
306 487
    private function resolveDiscriminatorValue(ClassMetadata $metadata)
307
    {
308 487
        if ($metadata->discriminatorValue
309 467
            || ! $metadata->discriminatorMap
310 52
            || $metadata->isMappedSuperclass
311 51
            || ! $metadata->reflClass
312 487
            || $metadata->reflClass->isAbstract()
313
        ) {
314 487
            return;
315
        }
316
317
        // minor optimization: avoid loading related metadata when not needed
318 5
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
319 5
            if ($discriminatorClass === $metadata->name) {
320 3
                $metadata->discriminatorValue = $discriminatorValue;
321
322 5
                return;
323
            }
324
        }
325
326
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
327 2
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
328 2
            if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) {
329 1
                $metadata->discriminatorValue = $discriminatorValue;
330
331 2
                return;
332
            }
333
        }
334
335 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName);
336
    }
337
338
    /**
339
     * Adds a default discriminator map if no one is given
340
     *
341
     * If an entity is of any inheritance type and does not contain a
342
     * discriminator map, then the map is generated automatically. This process
343
     * is expensive computation wise.
344
     *
345
     * The automatically generated discriminator map contains the lowercase short name of
346
     * each class as key.
347
     *
348
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
349
     *
350
     * @throws MappingException
351
     */
352
    private function addDefaultDiscriminatorMap(ClassMetadata $class)
353
    {
354
        $allClasses = $this->driver->getAllClassNames();
355
        $fqcn = $class->getName();
356
        $map = [$this->getShortName($class->name) => $fqcn];
357
358
        $duplicates = [];
359
        foreach ($allClasses as $subClassCandidate) {
360
            if (is_subclass_of($subClassCandidate, $fqcn)) {
361
                $shortName = $this->getShortName($subClassCandidate);
362
363
                if (isset($map[$shortName])) {
364
                    $duplicates[] = $shortName;
365
                }
366
367
                $map[$shortName] = $subClassCandidate;
368
            }
369
        }
370
371
        if ($duplicates) {
372
            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
373
        }
374
375
        $class->setDiscriminatorMap($map);
376
    }
377
378
    /**
379
     * Gets the lower-case short name of a class.
380
     *
381
     * @param string $className
382
     *
383
     * @return string
384
     */
385
    private function getShortName($className)
386
    {
387
        if (strpos($className, "\\") === false) {
388
            return strtolower($className);
389
        }
390
391
        $parts = explode("\\", $className);
392
393
        return strtolower(end($parts));
394
    }
395
396
    /**
397
     * Adds inherited fields to the subclass mapping.
398
     *
399
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
400
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
401
     *
402
     * @return void
403
     */
404 149
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
405
    {
406 149
        foreach ($parentClass->fieldMappings as $mapping) {
407 133
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
408 101
                $mapping['inherited'] = $parentClass->name;
409
            }
410 133
            if ( ! isset($mapping['declared'])) {
411 133
                $mapping['declared'] = $parentClass->name;
412
            }
413 133
            $subClass->addInheritedFieldMapping($mapping);
414
        }
415 149
        foreach ($parentClass->reflFields as $name => $field) {
416 134
            $subClass->reflFields[$name] = $field;
417
        }
418 149
    }
419
420
    /**
421
     * Adds inherited association mappings to the subclass mapping.
422
     *
423
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
424
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
425
     *
426
     * @return void
427
     *
428
     * @throws MappingException
429
     */
430 149
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
431
    {
432 149
        foreach ($parentClass->associationMappings as $field => $mapping) {
433 59
            if ($parentClass->isMappedSuperclass) {
434 16
                if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
435 1
                    throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field);
436
                }
437 15
                $mapping['sourceEntity'] = $subClass->name;
438
            }
439
440
            //$subclassMapping = $mapping;
441 58
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
442 43
                $mapping['inherited'] = $parentClass->name;
443
            }
444 58
            if ( ! isset($mapping['declared'])) {
445 58
                $mapping['declared'] = $parentClass->name;
446
            }
447 58
            $subClass->addInheritedAssociationMapping($mapping);
448
        }
449 148
    }
450
451 148
    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
452
    {
453 148
        foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
454 2
            if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
455 1
                $embeddedClass['inherited'] = $parentClass->name;
456
            }
457 2
            if ( ! isset($embeddedClass['declared'])) {
458 2
                $embeddedClass['declared'] = $parentClass->name;
459
            }
460
461 2
            $subClass->embeddedClasses[$field] = $embeddedClass;
462
        }
463 148
    }
464
465
    /**
466
     * Adds nested embedded classes metadata to a parent class.
467
     *
468
     * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.
469
     * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
470
     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
471
     */
472 12
    private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
473
    {
474 12
        foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
475 4
            if (isset($embeddableClass['inherited'])) {
476
                continue;
477
            }
478
479 4
            $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
480
481 4
            $parentClass->mapEmbedded(
482
                [
483 4
                    'fieldName' => $prefix . '.' . $property,
484 4
                    'class' => $embeddableMetadata->name,
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
485 4
                    'columnPrefix' => $embeddableClass['columnPrefix'],
486 4
                    'declaredField' => $embeddableClass['declaredField']
487 1
                            ? $prefix . '.' . $embeddableClass['declaredField']
488 4
                            : $prefix,
489 4
                    'originalField' => $embeddableClass['originalField'] ?: $property,
490
                ]
491
            );
492
        }
493 12
    }
494
495
    /**
496
     * Copy the table indices from the parent class superclass to the child class
497
     *
498
     * @param ClassMetadata $subClass
499
     * @param ClassMetadata $parentClass
500
     *
501
     * @return void
502
     */
503 148
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
504
    {
505 148
        if (! $parentClass->isMappedSuperclass) {
506 101
            return;
507
        }
508
509 52
        foreach (['uniqueConstraints', 'indexes'] as $indexType) {
510 52
            if (isset($parentClass->table[$indexType])) {
511 1
                foreach ($parentClass->table[$indexType] as $indexName => $index) {
512 1
                    if (isset($subClass->table[$indexType][$indexName])) {
513
                        continue; // Let the inheriting table override indices
514
                    }
515
516 52
                    $subClass->table[$indexType][$indexName] = $index;
517
                }
518
            }
519
        }
520 52
    }
521
522
    /**
523
     * Adds inherited named queries to the subclass mapping.
524
     *
525
     * @since 2.2
526
     *
527
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
528
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
529
     *
530
     * @return void
531
     */
532 1
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
533
    {
534 1
        foreach ($parentClass->namedQueries as $name => $query) {
535 1
            if ( ! isset ($subClass->namedQueries[$name])) {
536 1
                $subClass->addNamedQuery(
537
                    [
538 1
                        'name'  => $query['name'],
539 1
                        'query' => $query['query']
540
                    ]
541
                );
542
            }
543
        }
544 1
    }
545
546
    /**
547
     * Adds inherited named native queries to the subclass mapping.
548
     *
549
     * @since 2.3
550
     *
551
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
552
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
553
     *
554
     * @return void
555
     */
556 9
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
557
    {
558 9
        foreach ($parentClass->namedNativeQueries as $name => $query) {
559 9
            if ( ! isset ($subClass->namedNativeQueries[$name])) {
560 9
                $subClass->addNamedNativeQuery(
561
                    [
562 9
                        'name'              => $query['name'],
563 9
                        'query'             => $query['query'],
564 9
                        'isSelfClass'       => $query['isSelfClass'],
565 9
                        'resultSetMapping'  => $query['resultSetMapping'],
566 9
                        'resultClass'       => $query['isSelfClass'] ? $subClass->name : $query['resultClass'],
567
                    ]
568
                );
569
            }
570
        }
571 9
    }
572
573
    /**
574
     * Adds inherited sql result set mappings to the subclass mapping.
575
     *
576
     * @since 2.3
577
     *
578
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
579
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
580
     *
581
     * @return void
582
     */
583 9
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
584
    {
585 9
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
586 9
            if ( ! isset ($subClass->sqlResultSetMappings[$name])) {
587 9
                $entities = [];
588 9
                foreach ($mapping['entities'] as $entity) {
589 9
                    $entities[] = [
590 9
                        'fields'                => $entity['fields'],
591 9
                        'isSelfClass'           => $entity['isSelfClass'],
592 9
                        'discriminatorColumn'   => $entity['discriminatorColumn'],
593 9
                        'entityClass'           => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'],
594
                    ];
595
                }
596
597 9
                $subClass->addSqlResultSetMapping(
598
                    [
599 9
                        'name'          => $mapping['name'],
600 9
                        'columns'       => $mapping['columns'],
601 9
                        'entities'      => $entities,
602
                    ]
603
                );
604
            }
605
        }
606 9
    }
607
608
    /**
609
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
610
     * most appropriate for the targeted database platform.
611
     *
612
     * @param ClassMetadataInfo $class
613
     *
614
     * @return void
615
     *
616
     * @throws ORMException
617
     */
618 499
    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
619
    {
620 499
        $idGenType = $class->generatorType;
621 499
        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
622 355
            if ($this->getTargetPlatform()->prefersSequences()) {
623
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
624 355
            } else if ($this->getTargetPlatform()->prefersIdentityColumns()) {
625 355
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
626
            } else {
627
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
628
            }
629
        }
630
631
        // Create & assign an appropriate ID generator instance
632 499
        switch ($class->generatorType) {
633 499
            case ClassMetadata::GENERATOR_TYPE_IDENTITY:
634 368
                $sequenceName = null;
635 368
                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
636
637
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
638 368
                if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) {
639
                    $columnName     = $class->getSingleIdentifierColumnName();
640
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
641
                    $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform());
642
                    $sequenceName   = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName);
643
                    $definition     = [
644
                        'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName)
645
                    ];
646
647
                    if ($quoted) {
648
                        $definition['quoted'] = true;
649
                    }
650
651
                    $sequenceName = $this
652
                        ->em
653
                        ->getConfiguration()
654
                        ->getQuoteStrategy()
655
                        ->getSequenceName($definition, $class, $this->getTargetPlatform());
656
                }
657
658 368
                $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint')
659 1
                    ? new BigIntegerIdentityGenerator($sequenceName)
660 368
                    : new IdentityGenerator($sequenceName);
661
662 368
                $class->setIdGenerator($generator);
663
664 368
                break;
665
666 178
            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
667
                // If there is no sequence definition yet, create a default definition
668 6
                $definition = $class->sequenceGeneratorDefinition;
669
670 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...
671
                    $fieldName      = $class->getSingleIdentifierFieldName();
672
                    $sequenceName   = $class->getSequenceName($this->getTargetPlatform());
673
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
674
675
                    $definition = [
676
                        'sequenceName'      => $this->getTargetPlatform()->fixSchemaElementName($sequenceName),
677
                        'allocationSize'    => 1,
678
                        'initialValue'      => 1,
679
                    ];
680
681
                    if ($quoted) {
682
                        $definition['quoted'] = true;
683
                    }
684
685
                    $class->setSequenceGeneratorDefinition($definition);
686
                }
687
688 6
                $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
689 6
                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()),
690 6
                    $definition['allocationSize']
691
                );
692 6
                $class->setIdGenerator($sequenceGenerator);
693 6
                break;
694
695 175
            case ClassMetadata::GENERATOR_TYPE_NONE:
696 170
                $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
697 170
                break;
698
699 7
            case ClassMetadata::GENERATOR_TYPE_UUID:
700 2
                $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator());
701 2
                break;
702
703 5
            case ClassMetadata::GENERATOR_TYPE_TABLE:
704
                throw new ORMException("TableGenerator not yet implemented.");
705
                break;
706
707 5
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
708 5
                $definition = $class->customGeneratorDefinition;
709 5
                if ($definition === null) {
710 1
                    throw new ORMException("Can't instantiate custom generator : no custom generator definition");
711
                }
712 4
                if ( ! class_exists($definition['class'])) {
713 1
                    throw new ORMException("Can't instantiate custom generator : " .
714 1
                        $definition['class']);
715
                }
716 3
                $class->setIdGenerator(new $definition['class']);
717 3
                break;
718
719
            default:
720
                throw new ORMException("Unknown generator type: " . $class->generatorType);
721
        }
722 497
    }
723
724
    /**
725
     * Inherits the ID generator mapping from a parent class.
726
     *
727
     * @param ClassMetadataInfo $class
728
     * @param ClassMetadataInfo $parent
729
     */
730 106
    private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent)
731
    {
732 106
        if ($parent->isIdGeneratorSequence()) {
733 3
            $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
734 103
        } elseif ($parent->isIdGeneratorTable()) {
735
            $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition;
736
        }
737
738 106
        if ($parent->generatorType) {
739 106
            $class->setIdGeneratorType($parent->generatorType);
740
        }
741
742 106
        if ($parent->idGenerator) {
743 106
            $class->setIdGenerator($parent->idGenerator);
744
        }
745 106
    }
746
747
    /**
748
     * {@inheritDoc}
749
     */
750 2147
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
751
    {
752
        /* @var $class ClassMetadata */
753 2147
        $class->wakeupReflection($reflService);
754 2147
    }
755
756
    /**
757
     * {@inheritDoc}
758
     */
759 506
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
760
    {
761
        /* @var $class ClassMetadata */
762 506
        $class->initializeReflection($reflService);
763 506
    }
764
765
    /**
766
     * {@inheritDoc}
767
     */
768 4
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
769
    {
770 4
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
771
    }
772
773
    /**
774
     * {@inheritDoc}
775
     */
776 260
    protected function getDriver()
777
    {
778 260
        return $this->driver;
779
    }
780
781
    /**
782
     * {@inheritDoc}
783
     */
784 494
    protected function isEntity(ClassMetadataInterface $class)
785
    {
786 494
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
0 ignored issues
show
Bug introduced by
Accessing isMappedSuperclass on the interface Doctrine\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
787
    }
788
789
    /**
790
     * @return Platforms\AbstractPlatform
791
     */
792 374
    private function getTargetPlatform()
793
    {
794 374
        if (!$this->targetPlatform) {
795 374
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
796
        }
797
798 374
        return $this->targetPlatform;
799
    }
800
}
801
802
interface_exists(ClassMetadataInterface::class);
803
interface_exists(ReflectionService::class);
804