Failed Conditions
Push — master ( 2ade86...13f838 )
by Jonathan
18s
created

lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping;
21
22
use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory;
23
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
24
use Doctrine\Common\Persistence\Mapping\ReflectionService;
25
use Doctrine\DBAL\Platforms;
26
use Doctrine\ORM\EntityManagerInterface;
27
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
28
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
29
use Doctrine\ORM\Events;
30
use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
31
use Doctrine\ORM\Id\IdentityGenerator;
32
use Doctrine\ORM\ORMException;
33
use ReflectionException;
34
35
/**
36
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
37
 * metadata mapping information of a class which describes how a class should be mapped
38
 * to a relational database.
39
 *
40
 * @since   2.0
41
 * @author  Benjamin Eberlei <[email protected]>
42
 * @author  Guilherme Blanco <[email protected]>
43
 * @author  Jonathan Wage <[email protected]>
44
 * @author  Roman Borschel <[email protected]>
45
 */
46
class ClassMetadataFactory extends AbstractClassMetadataFactory
47
{
48
    /**
49
     * @var EntityManagerInterface|null
50
     */
51
    private $em;
52
53
    /**
54
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
55
     */
56
    private $targetPlatform;
57
58
    /**
59
     * @var \Doctrine\Common\Persistence\Mapping\Driver\MappingDriver
60
     */
61
    private $driver;
62
63
    /**
64
     * @var \Doctrine\Common\EventManager
65
     */
66
    private $evm;
67
68
    /**
69
     * @var array
70
     */
71
    private $embeddablesActiveNesting = [];
72
73
    /**
74
     * {@inheritDoc}
75
     */
76 475
    protected function loadMetadata($name)
77
    {
78 475
        $loaded = parent::loadMetadata($name);
79
80 456
        array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded));
81
82 456
        return $loaded;
83
    }
84
85
    /**
86
     * @param EntityManagerInterface $em
87
     */
88 2415
    public function setEntityManager(EntityManagerInterface $em)
89
    {
90 2415
        $this->em = $em;
91 2415
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96 544
    protected function initialize()
97
    {
98 544
        $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
99 544
        $this->evm = $this->em->getEventManager();
100 544
        $this->initialized = true;
101 544
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 3
    protected function onNotFoundMetadata($className)
107
    {
108 3
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
109 1
            return;
110
        }
111
112 2
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em);
113
114 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
115
116 2
        return $eventArgs->getFoundMetadata();
117
    }
118
119
    /**
120
     * {@inheritDoc}
121
     */
122 472
    protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents)
123
    {
124
        /* @var $class ClassMetadata */
125
        /* @var $parent ClassMetadata */
126 472
        if ($parent) {
127 145
            $class->setInheritanceType($parent->inheritanceType);
128 145
            $class->setDiscriminatorColumn($parent->discriminatorColumn);
129 145
            $class->setIdGeneratorType($parent->generatorType);
130 145
            $this->addInheritedFields($class, $parent);
131 145
            $this->addInheritedRelations($class, $parent);
132 144
            $this->addInheritedEmbeddedClasses($class, $parent);
133 144
            $class->setIdentifier($parent->identifier);
134 144
            $class->setVersioned($parent->isVersioned);
135 144
            $class->setVersionField($parent->versionField);
136 144
            $class->setDiscriminatorMap($parent->discriminatorMap);
137 144
            $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
138 144
            $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
139
140 144
            if ( ! empty($parent->customGeneratorDefinition)) {
141 1
                $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
142
            }
143
144 144
            if ($parent->isMappedSuperclass) {
145 53
                $class->setCustomRepositoryClass($parent->customRepositoryClassName);
146
            }
147
        }
148
149
        // Invoke driver
150
        try {
151 472
            $this->driver->loadMetadataForClass($class->getName(), $class);
152 2
        } catch (ReflectionException $e) {
153
            throw MappingException::reflectionFailure($class->getName(), $e);
154
        }
155
156
        // If this class has a parent the id generator strategy is inherited.
157
        // However this is only true if the hierarchy of parents contains the root entity,
158
        // if it consists of mapped superclasses these don't necessarily include the id field.
159 470
        if ($parent && $rootEntityFound) {
160 97
            $this->inheritIdGeneratorMapping($class, $parent);
161
        } else {
162 466
            $this->completeIdGeneratorMapping($class);
163
        }
164
165 468
        if (!$class->isMappedSuperclass) {
166 466
            foreach ($class->embeddedClasses as $property => $embeddableClass) {
167
168 14
                if (isset($embeddableClass['inherited'])) {
169 1
                    continue;
170
                }
171
172 14
                if ( ! (isset($embeddableClass['class']) && $embeddableClass['class'])) {
173 1
                    throw MappingException::missingEmbeddedClass($property);
174
                }
175
176 13
                if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
177 2
                    throw MappingException::infiniteEmbeddableNesting($class->name, $property);
178
                }
179
180 13
                $this->embeddablesActiveNesting[$class->name] = true;
181
182 13
                $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
183
184 12
                if ($embeddableMetadata->isEmbeddedClass) {
185 12
                    $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property);
186
                }
187
188 12
                $identifier = $embeddableMetadata->getIdentifier();
189
190 12
                if (! empty($identifier)) {
191 5
                    $this->inheritIdGeneratorMapping($class, $embeddableMetadata);
192
                }
193
194 12
                $class->inlineEmbeddable($property, $embeddableMetadata);
195
196 12
                unset($this->embeddablesActiveNesting[$class->name]);
197
            }
198
        }
199
200 465
        if ($parent) {
201 144
            if ($parent->isInheritanceTypeSingleTable()) {
202 37
                $class->setPrimaryTable($parent->table);
203
            }
204
205 144
            if ($parent) {
206 144
                $this->addInheritedIndexes($class, $parent);
207
            }
208
209 144
            if ($parent->cache) {
210 3
                $class->cache = $parent->cache;
211
            }
212
213 144
            if ($parent->containsForeignIdentifier) {
214
                $class->containsForeignIdentifier = true;
215
            }
216
217 144
            if ( ! empty($parent->namedQueries)) {
218 1
                $this->addInheritedNamedQueries($class, $parent);
219
            }
220
221 144
            if ( ! empty($parent->namedNativeQueries)) {
222 9
                $this->addInheritedNamedNativeQueries($class, $parent);
223
            }
224
225 144
            if ( ! empty($parent->sqlResultSetMappings)) {
226 9
                $this->addInheritedSqlResultSetMappings($class, $parent);
227
            }
228
229 144
            if ( ! empty($parent->entityListeners) && empty($class->entityListeners)) {
230 15
                $class->entityListeners = $parent->entityListeners;
231
            }
232
        }
233
234 465
        $class->setParentClasses($nonSuperclassParents);
235
236 465
        if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
237 1
            $this->addDefaultDiscriminatorMap($class);
238
        }
239
240 465
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
241 6
            $eventArgs = new LoadClassMetadataEventArgs($class, $this->em);
242 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
243
        }
244
245 464
        $this->validateRuntimeMetadata($class, $parent);
246 463
    }
247
248
    /**
249
     * Validate runtime metadata is correctly defined.
250
     *
251
     * @param ClassMetadata               $class
252
     * @param ClassMetadataInterface|null $parent
253
     *
254
     * @return void
255
     *
256
     * @throws MappingException
257
     */
258 464
    protected function validateRuntimeMetadata($class, $parent)
259
    {
260 464
        if ( ! $class->reflClass ) {
261
            // only validate if there is a reflection class instance
262 8
            return;
263
        }
264
265 456
        $class->validateIdentifier();
266 456
        $class->validateAssociations();
267 456
        $class->validateLifecycleCallbacks($this->getReflectionService());
268
269
        // verify inheritance
270 456
        if ( ! $class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
271 87
            if ( ! $parent) {
272 83
                if (count($class->discriminatorMap) == 0) {
273
                    throw MappingException::missingDiscriminatorMap($class->name);
274
                }
275 83
                if ( ! $class->discriminatorColumn) {
276 87
                    throw MappingException::missingDiscriminatorColumn($class->name);
277
                }
278
            }
279 411
        } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
280
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
281 1
            throw MappingException::noInheritanceOnMappedSuperClass($class->name);
282
        }
283 455
    }
284
285
    /**
286
     * {@inheritDoc}
287
     */
288 467
    protected function newClassMetadataInstance($className)
289
    {
290 467
        return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
291
    }
292
293
    /**
294
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
295
     * map classes and looking for a fitting one.
296
     *
297
     * @param ClassMetadata $metadata
298
     *
299
     * @return void
300
     *
301
     * @throws MappingException
302
     */
303 456
    private function resolveDiscriminatorValue(ClassMetadata $metadata)
304
    {
305 456
        if ($metadata->discriminatorValue
306 435
            || ! $metadata->discriminatorMap
307 49
            || $metadata->isMappedSuperclass
308 48
            || ! $metadata->reflClass
309 456
            || $metadata->reflClass->isAbstract()
310
        ) {
311 456
            return;
312
        }
313
314
        // minor optimization: avoid loading related metadata when not needed
315 5
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
316 5
            if ($discriminatorClass === $metadata->name) {
317 3
                $metadata->discriminatorValue = $discriminatorValue;
318
319 5
                return;
320
            }
321
        }
322
323
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
324 2
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
325 2
            if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) {
326 1
                $metadata->discriminatorValue = $discriminatorValue;
327
328 2
                return;
329
            }
330
        }
331
332 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName);
333
    }
334
335
    /**
336
     * Adds a default discriminator map if no one is given
337
     *
338
     * If an entity is of any inheritance type and does not contain a
339
     * discriminator map, then the map is generated automatically. This process
340
     * is expensive computation wise.
341
     *
342
     * The automatically generated discriminator map contains the lowercase short name of
343
     * each class as key.
344
     *
345
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
346
     *
347
     * @throws MappingException
348
     */
349 1
    private function addDefaultDiscriminatorMap(ClassMetadata $class)
350
    {
351 1
        $allClasses = $this->driver->getAllClassNames();
352 1
        $fqcn = $class->getName();
353 1
        $map = [$this->getShortName($class->name) => $fqcn];
354
355 1
        $duplicates = [];
356 1
        foreach ($allClasses as $subClassCandidate) {
357 1
            if (is_subclass_of($subClassCandidate, $fqcn)) {
358 1
                $shortName = $this->getShortName($subClassCandidate);
359
360 1
                if (isset($map[$shortName])) {
361
                    $duplicates[] = $shortName;
362
                }
363
364 1
                $map[$shortName] = $subClassCandidate;
365
            }
366
        }
367
368 1
        if ($duplicates) {
369
            throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
370
        }
371
372 1
        $class->setDiscriminatorMap($map);
373 1
    }
374
375
    /**
376
     * Gets the lower-case short name of a class.
377
     *
378
     * @param string $className
379
     *
380
     * @return string
381
     */
382 1
    private function getShortName($className)
383
    {
384 1
        if (strpos($className, "\\") === false) {
385
            return strtolower($className);
386
        }
387
388 1
        $parts = explode("\\", $className);
389
390 1
        return strtolower(end($parts));
391
    }
392
393
    /**
394
     * Adds inherited fields to the subclass mapping.
395
     *
396
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
397
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
398
     *
399
     * @return void
400
     */
401 145
    private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
402
    {
403 145 View Code Duplication
        foreach ($parentClass->fieldMappings as $mapping) {
404 129
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
405 97
                $mapping['inherited'] = $parentClass->name;
406
            }
407 129
            if ( ! isset($mapping['declared'])) {
408 129
                $mapping['declared'] = $parentClass->name;
409
            }
410 129
            $subClass->addInheritedFieldMapping($mapping);
411
        }
412 145
        foreach ($parentClass->reflFields as $name => $field) {
413 130
            $subClass->reflFields[$name] = $field;
414
        }
415 145
    }
416
417
    /**
418
     * Adds inherited association mappings to the subclass mapping.
419
     *
420
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
421
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
422
     *
423
     * @return void
424
     *
425
     * @throws MappingException
426
     */
427 145
    private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
428
    {
429 145
        foreach ($parentClass->associationMappings as $field => $mapping) {
430 58
            if ($parentClass->isMappedSuperclass) {
431 16
                if ($mapping['type'] & ClassMetadata::TO_MANY && !$mapping['isOwningSide']) {
432 1
                    throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->name, $field);
433
                }
434 15
                $mapping['sourceEntity'] = $subClass->name;
435
            }
436
437
            //$subclassMapping = $mapping;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
438 57
            if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
439 42
                $mapping['inherited'] = $parentClass->name;
440
            }
441 57
            if ( ! isset($mapping['declared'])) {
442 57
                $mapping['declared'] = $parentClass->name;
443
            }
444 57
            $subClass->addInheritedAssociationMapping($mapping);
445
        }
446 144
    }
447
448 144
    private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass)
449
    {
450 144 View Code Duplication
        foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
451 2
            if ( ! isset($embeddedClass['inherited']) && ! $parentClass->isMappedSuperclass) {
452 1
                $embeddedClass['inherited'] = $parentClass->name;
453
            }
454 2
            if ( ! isset($embeddedClass['declared'])) {
455 2
                $embeddedClass['declared'] = $parentClass->name;
456
            }
457
458 2
            $subClass->embeddedClasses[$field] = $embeddedClass;
459
        }
460 144
    }
461
462
    /**
463
     * Adds nested embedded classes metadata to a parent class.
464
     *
465
     * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.
466
     * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
467
     * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
468
     */
469 12
    private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, $prefix)
470
    {
471 12
        foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
472 4
            if (isset($embeddableClass['inherited'])) {
473
                continue;
474
            }
475
476 4
            $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']);
477
478 4
            $parentClass->mapEmbedded(
479
                [
480 4
                    'fieldName' => $prefix . '.' . $property,
481 4
                    'class' => $embeddableMetadata->name,
482 4
                    'columnPrefix' => $embeddableClass['columnPrefix'],
483 4
                    'declaredField' => $embeddableClass['declaredField']
484 1
                            ? $prefix . '.' . $embeddableClass['declaredField']
485 4
                            : $prefix,
486 4
                    'originalField' => $embeddableClass['originalField'] ?: $property,
487
                ]
488
            );
489
        }
490 12
    }
491
492
    /**
493
     * Copy the table indices from the parent class superclass to the child class
494
     *
495
     * @param ClassMetadata $subClass
496
     * @param ClassMetadata $parentClass
497
     *
498
     * @return void
499
     */
500 144
    private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass)
501
    {
502 144
        if (! $parentClass->isMappedSuperclass) {
503 97
            return;
504
        }
505
506 53
        foreach (['uniqueConstraints', 'indexes'] as $indexType) {
507 53
            if (isset($parentClass->table[$indexType])) {
508 1
                foreach ($parentClass->table[$indexType] as $indexName => $index) {
509 1
                    if (isset($subClass->table[$indexType][$indexName])) {
510
                        continue; // Let the inheriting table override indices
511
                    }
512
513 53
                    $subClass->table[$indexType][$indexName] = $index;
514
                }
515
            }
516
        }
517 53
    }
518
519
    /**
520
     * Adds inherited named queries to the subclass mapping.
521
     *
522
     * @since 2.2
523
     *
524
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
525
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
526
     *
527
     * @return void
528
     */
529 1
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
530
    {
531 1
        foreach ($parentClass->namedQueries as $name => $query) {
532 1
            if ( ! isset ($subClass->namedQueries[$name])) {
533 1
                $subClass->addNamedQuery(
534
                    [
535 1
                        'name'  => $query['name'],
536 1
                        'query' => $query['query']
537
                    ]
538
                );
539
            }
540
        }
541 1
    }
542
543
    /**
544
     * Adds inherited named native queries to the subclass mapping.
545
     *
546
     * @since 2.3
547
     *
548
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
549
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
550
     *
551
     * @return void
552
     */
553 9
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass)
554
    {
555 9
        foreach ($parentClass->namedNativeQueries as $name => $query) {
556 9
            if ( ! isset ($subClass->namedNativeQueries[$name])) {
557 9
                $subClass->addNamedNativeQuery(
558
                    [
559 9
                        'name'              => $query['name'],
560 9
                        'query'             => $query['query'],
561 9
                        'isSelfClass'       => $query['isSelfClass'],
562 9
                        'resultSetMapping'  => $query['resultSetMapping'],
563 9
                        'resultClass'       => $query['isSelfClass'] ? $subClass->name : $query['resultClass'],
564
                    ]
565
                );
566
            }
567
        }
568 9
    }
569
570
    /**
571
     * Adds inherited sql result set mappings to the subclass mapping.
572
     *
573
     * @since 2.3
574
     *
575
     * @param \Doctrine\ORM\Mapping\ClassMetadata $subClass
576
     * @param \Doctrine\ORM\Mapping\ClassMetadata $parentClass
577
     *
578
     * @return void
579
     */
580 9
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass)
581
    {
582 9
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
583 9
            if ( ! isset ($subClass->sqlResultSetMappings[$name])) {
584 9
                $entities = [];
585 9
                foreach ($mapping['entities'] as $entity) {
586 9
                    $entities[] = [
587 9
                        'fields'                => $entity['fields'],
588 9
                        'isSelfClass'           => $entity['isSelfClass'],
589 9
                        'discriminatorColumn'   => $entity['discriminatorColumn'],
590 9
                        'entityClass'           => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass'],
591
                    ];
592
                }
593
594 9
                $subClass->addSqlResultSetMapping(
595
                    [
596 9
                        'name'          => $mapping['name'],
597 9
                        'columns'       => $mapping['columns'],
598 9
                        'entities'      => $entities,
599
                    ]
600
                );
601
            }
602
        }
603 9
    }
604
605
    /**
606
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
607
     * most appropriate for the targeted database platform.
608
     *
609
     * @param ClassMetadataInfo $class
610
     *
611
     * @return void
612
     *
613
     * @throws ORMException
614
     */
615 466
    private function completeIdGeneratorMapping(ClassMetadataInfo $class)
616
    {
617 466
        $idGenType = $class->generatorType;
618 466
        if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
619 334
            if ($this->getTargetPlatform()->prefersSequences()) {
620
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
621 334
            } else if ($this->getTargetPlatform()->prefersIdentityColumns()) {
622 334
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
623
            } else {
624
                $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
625
            }
626
        }
627
628
        // Create & assign an appropriate ID generator instance
629 466
        switch ($class->generatorType) {
630 466
            case ClassMetadata::GENERATOR_TYPE_IDENTITY:
631 347
                $sequenceName = null;
632 347
                $fieldName    = $class->identifier ? $class->getSingleIdentifierFieldName() : null;
633
634
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
635 347
                if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) {
636
                    $columnName     = $class->getSingleIdentifierColumnName();
637
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
638
                    $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform());
639
                    $sequenceName   = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName);
640
                    $definition     = [
641
                        'sequenceName' => $this->getTargetPlatform()->fixSchemaElementName($sequenceName)
642
                    ];
643
644
                    if ($quoted) {
645
                        $definition['quoted'] = true;
646
                    }
647
648
                    $sequenceName = $this
649
                        ->em
650
                        ->getConfiguration()
651
                        ->getQuoteStrategy()
652
                        ->getSequenceName($definition, $class, $this->getTargetPlatform());
653
                }
654
655 347
                $generator = ($fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint')
656 1
                    ? new BigIntegerIdentityGenerator($sequenceName)
657 347
                    : new IdentityGenerator($sequenceName);
658
659 347
                $class->setIdGenerator($generator);
660
661 347
                break;
662
663 165
            case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
664
                // If there is no sequence definition yet, create a default definition
665 6
                $definition = $class->sequenceGeneratorDefinition;
666
667 6
                if ( ! $definition) {
668
                    $fieldName      = $class->getSingleIdentifierFieldName();
669
                    $sequenceName   = $class->getSequenceName($this->getTargetPlatform());
670
                    $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
671
672
                    $definition = [
673
                        'sequenceName'      => $this->getTargetPlatform()->fixSchemaElementName($sequenceName),
674
                        'allocationSize'    => 1,
675
                        'initialValue'      => 1,
676
                    ];
677
678
                    if ($quoted) {
679
                        $definition['quoted'] = true;
680
                    }
681
682
                    $class->setSequenceGeneratorDefinition($definition);
683
                }
684
685 6
                $sequenceGenerator = new \Doctrine\ORM\Id\SequenceGenerator(
686 6
                    $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()),
687 6
                    $definition['allocationSize']
688
                );
689 6
                $class->setIdGenerator($sequenceGenerator);
690 6
                break;
691
692 162
            case ClassMetadata::GENERATOR_TYPE_NONE:
693 157
                $class->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
694 157
                break;
695
696 7
            case ClassMetadata::GENERATOR_TYPE_UUID:
697 2
                $class->setIdGenerator(new \Doctrine\ORM\Id\UuidGenerator());
698 2
                break;
699
700 5
            case ClassMetadata::GENERATOR_TYPE_TABLE:
701
                throw new ORMException("TableGenerator not yet implemented.");
702
                break;
703
704 5
            case ClassMetadata::GENERATOR_TYPE_CUSTOM:
705 5
                $definition = $class->customGeneratorDefinition;
706 5
                if ( ! class_exists($definition['class'])) {
707 2
                    throw new ORMException("Can't instantiate custom generator : " .
708 2
                        $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 464
    }
717
718
    /**
719
     * Inherits the ID generator mapping from a parent class.
720
     *
721
     * @param ClassMetadataInfo $class
722
     * @param ClassMetadataInfo $parent
723
     */
724 102
    private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent)
725
    {
726 102
        if ($parent->isIdGeneratorSequence()) {
727 3
            $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
728 99
        } elseif ($parent->isIdGeneratorTable()) {
729
            $class->tableGeneratorDefinition = $parent->tableGeneratorDefinition;
730
        }
731
732 102
        if ($parent->generatorType) {
733 102
            $class->setIdGeneratorType($parent->generatorType);
734
        }
735
736 102
        if ($parent->idGenerator) {
737 102
            $class->setIdGenerator($parent->idGenerator);
738
        }
739 102
    }
740
741
    /**
742
     * {@inheritDoc}
743
     */
744 2080
    protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService)
745
    {
746
        /* @var $class ClassMetadata */
747 2080
        $class->wakeupReflection($reflService);
748 2080
    }
749
750
    /**
751
     * {@inheritDoc}
752
     */
753 472
    protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService)
754
    {
755
        /* @var $class ClassMetadata */
756 472
        $class->initializeReflection($reflService);
757 472
    }
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 255
    protected function getDriver()
771
    {
772 255
        return $this->driver;
773
    }
774
775
    /**
776
     * {@inheritDoc}
777
     */
778 463
    protected function isEntity(ClassMetadataInterface $class)
779
    {
780 463
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
781
    }
782
783
    /**
784
     * @return Platforms\AbstractPlatform
785
     */
786 353
    private function getTargetPlatform()
787
    {
788 353
        if (!$this->targetPlatform) {
789 353
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
790
        }
791
792 353
        return $this->targetPlatform;
793
    }
794
}
795