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

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

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