Failed Conditions
Pull Request — master (#7878)
by
unknown
61:32
created

ClassMetadataFactory::doLoadMetadata()   F

Complexity

Conditions 29
Paths > 20000

Size

Total Lines 124
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 29.1445

Importance

Changes 0
Metric Value
eloc 66
c 0
b 0
f 0
dl 0
loc 124
rs 0
ccs 34
cts 36
cp 0.9444
cc 29
nc 20585
nop 4
crap 29.1445

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 2276
74
    /**
75 2276
     * {@inheritDoc}
76 2276
     */
77
    protected function loadMetadata($name)
78
    {
79
        $loaded = parent::loadMetadata($name);
80
81 2274
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
82
83 2274
        return $loaded;
84 2274
    }
85
86
    /**
87
     * @param EntityManagerInterface $em
88
     */
89
    public function setEntityManager(EntityManagerInterface $em)
90
    {
91
        $this->em = $em;
92
    }
93
94
    /**
95
     * {@inheritDoc}
96
     */
97
    protected function initialize()
98
    {
99
        $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
        $this->evm = $this->em->getEventManager();
101
        $this->initialized = true;
102
    }
103
104
    /**
105
     * {@inheritDoc}
106
     */
107
    protected function onNotFoundMetadata($className)
108
    {
109
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
110
            return;
111
        }
112
113
        $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 1964
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
116
117 1964
        return $eventArgs->getFoundMetadata();
118 1964
    }
119
120
    /**
121 1964
     * {@inheritDoc}
122
     */
123
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
124
    {
125
        /* @var $class ClassMetadata */
126
        /* @var $parent ClassMetadata */
127 48
        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
            $class->setInheritanceType($parent->inheritanceType);
129 48
            $class->setDiscriminatorColumn($parent->discriminatorColumn);
130 15
            $class->setIdGeneratorType($parent->generatorType);
131
            $this->addInheritedFields($class, $parent);
132
            $this->addInheritedRelations($class, $parent);
133 48
            $this->addInheritedEmbeddedClasses($class, $parent);
134
            $class->setIdentifier($parent->identifier);
135 48
            $class->setVersioned($parent->isVersioned);
136
            $class->setVersionField($parent->versionField);
137
            $class->setDiscriminatorMap($parent->discriminatorMap);
138
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
139
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
140
141
            if ( ! empty($parent->customGeneratorDefinition)) {
142
                $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
143
            }
144
145 66
            if ($parent->isMappedSuperclass) {
146
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
147 66
            }
148
        }
149
150
        // Invoke driver
151
        try {
152
            $this->driver->loadMetadataForClass($class->getName(), $class);
153
        } 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 5
        // 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 5
        if ($parent && $rootEntityFound) {
161 5
            $this->inheritIdGeneratorMapping($class, $parent);
162
        } else {
163
            $this->completeIdGeneratorMapping($class);
164
        }
165
166
        if (!$class->isMappedSuperclass) {
167
            foreach ($class->embeddedClasses as $property => $embeddableClass) {
168
169
                if (isset($embeddableClass['inherited'])) {
170
                    continue;
171
                }
172
173
                if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) {
174
                    throw MappingException::missingEmbeddedClass($property);
175 56
                }
176
177 56
                if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
178 56
                    throw MappingException::infiniteEmbeddableNesting($class->name, $property);
179
                }
180
181 56
                $this->embeddablesActiveNesting[$class->name] = true;
182 56
183
                $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
184 56
185 55
                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
                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
187
                }
188 56
189
                $identifier = $embeddableMetadata->getIdentifier();
190
191
                if (! empty($identifier)) {
192
                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);
193
                }
194
195
                $class->inlineEmbeddable($property, $embeddableMetadata);
196 452
197
                unset($this->embeddablesActiveNesting[$class->name]);
198 452
            }
199 452
        }
200 452
201 452
        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
            if ($parent->isInheritanceTypeSingleTable()) {
203
                $class->setPrimaryTable($parent->table);
204
            }
205
206 199
            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
                $this->addInheritedIndexes($class, $parent);
208 199
            }
209
210
            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 1963
                $class->cache = $parent->cache;
212
            }
213 1963
214 1963
            if ($parent->containsForeignIdentifier) {
215
                $class->containsForeignIdentifier = true;
216
            }
217 1963
218
            if ( ! empty($parent->namedQueries)) {
219
                $this->addInheritedNamedQueries($class, $parent);
220
            }
221
222
            if ( ! empty($parent->namedNativeQueries)) {
223
                $this->addInheritedNamedNativeQueries($class, $parent);
224
            }
225
226
            if ( ! empty($parent->sqlResultSetMappings)) {
227
                $this->addInheritedSqlResultSetMappings($class, $parent);
228
            }
229
230
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
231
                $class->entityListeners = $parent->entityListeners;
232
            }
233
        }
234
235
        $class->setParentClasses($nonSuperclassParents);
236
237 1969
        if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
238
            $this->addDefaultDiscriminatorMap($class);
239 1969
        }
240 1647
241
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
242
            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
243 1964
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
244
        }
245 1964
246
        $this->validateRuntimeMetadata($class, $parent);
247 214
    }
248
249
    /**
250 1964
     * Validate runtime metadata is correctly defined.
251 1964
     *
252
     * @param ClassMetadata               $class
253
     * @param ClassMetadataInterface|null $parent
254 1964
     *
255 1860
     * @return void
256
     *
257 1860
     * @throws MappingException
258 1632
     */
259
    protected function validateRuntimeMetadata($class, $parent)
260 1632
    {
261
        if ( ! $class->reflClass ) {
262 1860
            // only validate if there is a reflection class instance
263 268
            return;
264
        }
265 268
266
        $class->validateIdentifier();
267
        $class->validateAssociations();
268
        $class->validateLifecycleCallbacks($this->getReflectionService());
269 1956
270
        // verify inheritance
271 24
        if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
272 12
            if ( ! $parent) {
273
                if (count($class->discriminatorMap) == 0) {
274 12
                    throw MappingException::missingDiscriminatorMap($class->name);
275 10
                }
276
                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
                    throw MappingException::missingDiscriminatorColumn($class->name);
278 2
                }
279
            }
280
        } 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 1948
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
282
            throw MappingException::noInheritanceOnMappedSuperClass($class->name);
283 3
        }
284
    }
285
286 1948
    /**
287
     * {@inheritDoc}
288 1948
     */
289
    protected function newClassMetadataInstance($className)
290
    {
291
        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
    private function resolveDiscriminatorValue(ClassMetadata $metadata)
305
    {
306
        if ($metadata->discriminatorValue
307
            || ! $metadata->discriminatorMap
308 386
            || $metadata->isMappedSuperclass
309
            || ! $metadata->reflClass
310 386
            || $metadata->reflClass->isAbstract()
311 381
        ) {
312
            return;
313
        }
314 386
315 386
        // minor optimization: avoid loading related metadata when not needed
316 375
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
317
            if ($discriminatorClass === $metadata->name) {
318
                $metadata->discriminatorValue = $discriminatorValue;
319 375
320
                return;
321 375
            }
322 375
        }
323 72
324
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
325 72
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
326
            if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) {
327
                $metadata->discriminatorValue = $discriminatorValue;
328 375
329
                return;
330 366
            }
331
        }
332 366
333 366
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName);
334
    }
335
336 364
    /**
337
     * Adds a default discriminator map if no one is given
338 364
     *
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 386
    private function addDefaultDiscriminatorMap(ClassMetadata $class)
351
    {
352
        $allClasses = $this->driver->getAllClassNames();
353 386
        $fqcn = $class->getName();
354
        $map = [$this->getShortName($class->name) => $fqcn];
355 386
356 105
        $duplicates = [];
357 100
        foreach ($allClasses as $subClassCandidate) {
358
            if (is_subclass_of($subClassCandidate, $fqcn)) {
359
                $shortName = $this->getShortName($subClassCandidate);
360
361 375
                if (isset($map[$shortName])) {
362
                    $duplicates[] = $shortName;
363
                }
364
365
                $map[$shortName] = $subClassCandidate;
366
            }
367 12
        }
368
369
        if ($duplicates) {
370
            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
371 12
        }
372 10
373
        $class->setDiscriminatorMap($map);
374
    }
375 2
376
    /**
377 2
     * Gets the lower-case short name of a class.
378
     *
379 2
     * @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 374
389
        $parts = explode("\\", $className);
390
391
        return strtolower(end($parts));
392
    }
393 374
394 374
    /**
395 374
     * Adds inherited fields to the subclass mapping.
396
     *
397
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
398 374
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
399
     *
400 368
     * @return void
401 6
     */
402
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
403 6
    {
404
        foreach ($parentClass->fieldMappings as $mapping) {
405
            if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass && ! $parentClass->isEmbeddedClass) {
406 368
                $mapping['inherited'] = $parentClass->name;
407
            }
408 368
            if (! isset($mapping['declared'])) {
409 368
                $mapping['declared'] = $parentClass->name;
410
            }
411 365
            $subClass->addInheritedFieldMapping($mapping);
412
        }
413
        foreach ($parentClass->reflFields as $name => $field) {
414
            $subClass->reflFields[$name] = $field;
415
        }
416
    }
417
418
    /**
419 368
     * Adds inherited association mappings to the subclass mapping.
420
     *
421 368
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
422
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
423
     *
424
     * @return void
425
     *
426 368
     * @throws MappingException
427 368
     */
428
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
429
    {
430 368
        foreach ($parentClass->associationMappings as $field => $mapping) {
431 77
            if ($parentClass->isMappedSuperclass) {
432 76
                if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
433 3
                    throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field);
434
                }
435
                $mapping['sourceEntity'] = $subClass->name;
436 73
            }
437 74
438
            //$subclassMapping = $mapping;
439
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
440 329
                $mapping['inherited'] = $parentClass->name;
441
            }
442
            if ( ! isset($mapping['declared'])) {
443
                $mapping['declared'] = $parentClass->name;
444 365
            }
445
            $subClass->addInheritedAssociationMapping($mapping);
446
        }
447
    }
448
449 1964
    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
450
    {
451 1964
        foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
452 1964
            if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
453 1964
                $embeddedClass['inherited'] = $parentClass->name;
454 1964
            }
455 1964
            if ( ! isset($embeddedClass['declared'])) {
456
                $embeddedClass['declared'] = $parentClass->name;
457
            }
458
459
            $subClass->embeddedClasses[$field] = $embeddedClass;
460
        }
461
    }
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 364
     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
469
     */
470 364
    private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
471 364
    {
472 364
        foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
473
            $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
474
475
            $parentClass->mapEmbedded(
476 4
                [
477 4
                    'fieldName' => $prefix . '.' . $property,
478 3
                    '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
                    'columnPrefix' => $embeddableClass['columnPrefix'],
480 3
                    'declaredField' => $embeddableClass['declaredField']
481
                            ? $prefix . '.' . $embeddableClass['declaredField']
482
                            : $prefix,
483
                    'originalField' => $embeddableClass['originalField'] ?: $property,
484
                ]
485 1
            );
486 1
        }
487
    }
488
489
    /**
490
     * Copy the table indices from the parent class superclass to the child class
491
     *
492
     * @param ClassMetadata $subClass
493 1
     * @param ClassMetadata $parentClass
494
     *
495
     * @return void
496 368
     */
497
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
498 368
    {
499
        if (! $parentClass->isMappedSuperclass) {
500 368
            return;
501 368
        }
502
503 368
        foreach (['uniqueConstraints', 'indexes'] as $indexType) {
504 309
            if (isset($parentClass->table[$indexType])) {
505
                foreach ($parentClass->table[$indexType] as $indexName => $index) {
506
                    if (isset($subClass->table[$indexType][$indexName])) {
507
                        continue; // Let the inheriting table override indices
508 368
                    }
509 368
510 90
                    $subClass->table[$indexType][$indexName] = $index;
511 90
                }
512
            }
513 309
        }
514 304
    }
515 304
516
    /**
517
     * Adds inherited named queries to the subclass mapping.
518 18
     *
519 18
     * @since 2.2
520
     *
521
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
522 368
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
523 368
     *
524
     * @return void
525
     */
526
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
527
    {
528
        foreach ($parentClass->namedQueries as $name => $query) {
529
            if ( ! isset ($subClass->namedQueries[$name])) {
530
                $subClass->addNamedQuery(
531
                    [
532
                        'name'  => $query['name'],
533
                        'query' => $query['query']
534
                    ]
535
                );
536
            }
537
        }
538
    }
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
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
551
    {
552
        foreach ($parentClass->namedNativeQueries as $name => $query) {
553
            if ( ! isset ($subClass->namedNativeQueries[$name])) {
554
                $subClass->addNamedNativeQuery(
555
                    [
556
                        'name'              => $query['name'],
557
                        'query'             => $query['query'],
558
                        'isSelfClass'       => $query['isSelfClass'],
559
                        'resultSetMapping'  => $query['resultSetMapping'],
560
                        'resultClass'       => $query['isSelfClass'] ? $subClass->name : $query['resultClass'],
561
                    ]
562
                );
563
            }
564
        }
565
    }
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
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
578
    {
579
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
580
            if ( ! isset ($subClass->sqlResultSetMappings[$name])) {
581
                $entities = [];
582
                foreach ($mapping['entities'] as $entity) {
583
                    $entities[] = [
584
                        'fields'                => $entity['fields'],
585
                        'isSelfClass'           => $entity['isSelfClass'],
586
                        'discriminatorColumn'   => $entity['discriminatorColumn'],
587
                        'entityClass'           => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'],
588
                    ];
589
                }
590
591
                $subClass->addSqlResultSetMapping(
592
                    [
593
                        'name'          => $mapping['name'],
594
                        'columns'       => $mapping['columns'],
595
                        'entities'      => $entities,
596
                    ]
597
                );
598
            }
599
        }
600
    }
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
    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
613
    {
614
        $idGenType = $class->generatorType;
615
        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
616
            if ($this->getTargetPlatform()->prefersSequences()) {
617
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
618
            } else if ($this->getTargetPlatform()->prefersIdentityColumns()) {
619
                $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
        switch ($class->generatorType) {
627
            case ClassMetadata::GENERATOR_TYPE_IDENTITY:
628
                $sequenceName = null;
629
                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
630
631
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
632
                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
                $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint')
653
                    ? new BigIntegerIdentityGenerator($sequenceName)
654
                    : new IdentityGenerator($sequenceName);
655
656
                $class->setIdGenerator($generator);
657
658
                break;
659
660
            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
661
                // If there is no sequence definition yet, create a default definition
662
                $definition = $class->sequenceGeneratorDefinition;
663
664
                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
                $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
683
                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()),
684
                    $definition['allocationSize']
685
                );
686
                $class->setIdGenerator($sequenceGenerator);
687
                break;
688
689
            case ClassMetadata::GENERATOR_TYPE_NONE:
690
                $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
691
                break;
692
693
            case ClassMetadata::GENERATOR_TYPE_UUID:
694
                $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator());
695
                break;
696
697
            case ClassMetadata::GENERATOR_TYPE_TABLE:
698
                throw new ORMException("TableGenerator not yet implemented.");
699
                break;
700
701
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
702
                $definition = $class->customGeneratorDefinition;
703
                if ($definition === null) {
704
                    throw new ORMException("Can't instantiate custom generator : no custom generator definition");
705
                }
706
                if ( ! class_exists($definition['class'])) {
707
                    throw new ORMException("Can't instantiate custom generator : " .
708
                        $definition['class']);
709
                }
710
                $class->setIdGenerator(new $definition['class']);
711
                break;
712
713
            default:
714
                throw new ORMException("Unknown generator type: " . $class->generatorType);
715
        }
716
    }
717
718
    /**
719
     * Inherits the ID generator mapping from a parent class.
720
     *
721
     * @param ClassMetadataInfo $class
722
     * @param ClassMetadataInfo $parent
723
     */
724
    private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent)
725
    {
726
        if ($parent->isIdGeneratorSequence()) {
727
            $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
728
        } elseif ($parent->isIdGeneratorTable()) {
729
            $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition;
730
        }
731
732
        if ($parent->generatorType) {
733
            $class->setIdGeneratorType($parent->generatorType);
734
        }
735
736
        if ($parent->idGenerator) {
737
            $class->setIdGenerator($parent->idGenerator);
738
        }
739
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
745
    {
746
        /* @var $class ClassMetadata */
747
        $class->wakeupReflection($reflService);
748
    }
749
750
    /**
751
     * {@inheritDoc}
752
     */
753
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
754
    {
755
        /* @var $class ClassMetadata */
756
        $class->initializeReflection($reflService);
757
    }
758
759
    /**
760
     * {@inheritDoc}
761
     */
762
    protected function getFqcnFromAlias($namespaceAlias, $simpleClassName)
763
    {
764
        return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
765
    }
766
767
    /**
768
     * {@inheritDoc}
769
     */
770
    protected function getDriver()
771
    {
772
        return $this->driver;
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778
    protected function isEntity(ClassMetadataInterface $class)
779
    {
780
        assert($class instanceof ClassMetadata);
781
782
        return $class->isMappedSuperclass === false && $class->isEmbeddedClass === false;
783
    }
784
785
    /**
786
     * @return Platforms\AbstractPlatform
787
     */
788
    private function getTargetPlatform()
789
    {
790
        if (!$this->targetPlatform) {
791
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
792
        }
793
794
        return $this->targetPlatform;
795
    }
796
}
797