Failed Conditions
Pull Request — 2.7 (#8036)
by Benjamin
06:53
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\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