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

ClassMetadataFactory::addInheritedFields()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 8
c 1
b 1
f 0
dl 0
loc 13
ccs 9
cts 9
cp 1
rs 9.2222
cc 6
nc 10
nop 2
crap 6
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping;
21
22
use Doctrine\DBAL\Platforms;
23
use Doctrine\ORM\EntityManagerInterface;
24
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
25
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
26
use Doctrine\ORM\Events;
27
use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
28
use Doctrine\ORM\Id\IdentityGenerator;
29
use Doctrine\ORM\ORMException;
30
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
31
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
32
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
33
use Doctrine\Persistence\Mapping\ReflectionService;
34
use ReflectionException;
35
use function assert;
36
use function interface_exists;
37
38
/**
39
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
40
 * metadata mapping information of a class which describes how a class should be mapped
41
 * to a relational database.
42
 *
43
 * @since   2.0
44
 * @author  Benjamin Eberlei <[email protected]>
45
 * @author  Guilherme Blanco <[email protected]>
46
 * @author  Jonathan Wage <[email protected]>
47
 * @author  Roman Borschel <[email protected]>
48
 */
49
class ClassMetadataFactory extends AbstractClassMetadataFactory
50
{
51
    /**
52
     * @var EntityManagerInterface|null
53
     */
54
    private $em;
55
56
    /**
57
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
58
     */
59
    private $targetPlatform;
60
61
    /**
62
     * @var MappingDriver
63
     */
64
    private $driver;
65
66
    /**
67
     * @var \Doctrine\Common\EventManager
68
     */
69
    private $evm;
70
71
    /**
72
     * @var array
73
     */
74
    private $embeddablesActiveNesting = [];
75
76
    /**
77
     * {@inheritDoc}
78
     */
79 509
    protected function loadMetadata($name)
80
    {
81 509
        $loaded = parent::loadMetadata($name);
82
83 487
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
84
85 487
        return $loaded;
86
    }
87
88
    /**
89
     * @param EntityManagerInterface $em
90
     */
91 2509
    public function setEntityManager(EntityManagerInterface $em)
92
    {
93 2509
        $this->em = $em;
94 2509
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 579
    protected function initialize()
100
    {
101 579
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
0 ignored issues
show
Bug introduced by
The method getConfiguration() does not exist on null. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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