Passed
Push — 2.7 ( cd905f...a9b6b7 )
by Benjamin
07:25
created

ClassMetadataFactory::addNestedEmbeddedClasses()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 14
rs 9.9332
ccs 11
cts 11
cp 1
cc 4
nc 2
nop 3
crap 4
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