Failed Conditions
Pull Request — develop (#6873)
by
unknown
112:44 queued 47:41
created

NewAnnotationDriver::getCascade()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 3
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping\Driver;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Doctrine\Common\Persistence\Mapping\Driver\FileLocator;
9
use Doctrine\DBAL\Types\Type;
10
use Doctrine\ORM\Annotation;
11
use Doctrine\ORM\Events;
12
use Doctrine\ORM\Mapping;
13
use Doctrine\ORM\Mapping\Factory;
14
15
class NewAnnotationDriver implements MappingDriver
16
{
17
    static protected $entityAnnotationClasses = [
18
        Annotation\Entity::class           => 1,
19
        Annotation\MappedSuperclass::class => 2,
20
    ];
21
22
    /**
23
     * The Annotation reader.
24
     *
25
     * @var AnnotationReader
26
     */
27
    protected $reader;
28
29
    /**
30
     * The file locator.
31
     *
32
     * @var FileLocator
33
     */
34
    protected $locator;
35
36
    /**
37
     * @var Factory\NamingStrategy
38
     */
39
    protected $namingStrategy;
40
41
    /**
42
     * Cache for AnnotationDriver#getAllClassNames().
43
     *
44
     * @var array|null
45
     */
46
    private $classNames;
47
48
    /**
49
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations.
50
     *
51
     * @param AnnotationReader       $reader         The AnnotationReader to use, duck-typed.
52
     * @param FileLocator            $locator        A FileLocator or one/multiple paths where mapping documents can be found.
53
     * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use.
54
     */
55
    public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy)
56
    {
57
        $this->reader         = $reader;
58
        $this->locator        = $locator;
59
        $this->namingStrategy = $namingStrategy;
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     *
65
     * @return Mapping\ClassMetadata
66
     *
67
     * @throws Mapping\MappingException
68
     */
69
    public function loadMetadataForClass(
70
        string $className,
71
        Mapping\ClassMetadata $metadata,
72
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
73
    )
74
    {
75
        // IMPORTANT: We're handling $metadata as "parent" metadata here, while building the $className ClassMetadata.
76
        $reflectionClass  = new \ReflectionClass($className);
77
78
        // Evaluate annotations on class metadata
79
        $classAnnotations = $this->getClassAnnotations($reflectionClass);
80
        $classMetadata    = $this->convertClassAnnotationsToClassMetadata(
81
            $classAnnotations,
82
            $reflectionClass,
83
            $metadata
84
        );
85
86
        // Evaluate @Cache annotation
87 View Code Duplication
        if (isset($classAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88
            $cacheAnnot = $classAnnotations[Annotation\Cache::class];
89
            $cache      = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata);
90
91
            $classMetadata->setCache($cache);
92
        }
93
94
        // Evaluate annotations on properties/fields
95
        /* @var \ReflectionProperty $reflectionProperty */
96
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
97
            if ($reflectionProperty->getDeclaringClass()->getClassName() !== $reflectionClass->getName()) {
0 ignored issues
show
Bug introduced by
The method getClassName() does not exist on ReflectionClass. ( Ignorable by Annotation )

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

97
            if ($reflectionProperty->getDeclaringClass()->/** @scrutinizer ignore-call */ getClassName() !== $reflectionClass->getName()) {

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...
98
                continue;
99
            }
100
101
            $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty);
102
            $property            = $this->convertReflectionPropertyAnnotationsToProperty(
103
                $reflectionProperty,
104
                $propertyAnnotations,
105
                $classMetadata
106
            );
107
108
            $classMetadata->addDeclaredProperty($property);
109
        }
110
111
        return $classMetadata;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $classMetadata returns the type Doctrine\ORM\Mapping\ClassMetadata which is incompatible with the return type mandated by Doctrine\ORM\Mapping\Dri...:loadMetadataForClass() of void.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117
    public function getAllClassNames()
118
    {
119
        if ($this->classNames !== null) {
120
            return $this->classNames;
121
        }
122
123
        $classNames = array_filter(
124
            $this->locator->getAllClassNames(null),
125
            function ($className) {
126
                return ! $this->isTransient($className);
127
            }
128
        );
129
130
        $this->classNames = $classNames;
131
132
        return $classNames;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function isTransient($className)
139
    {
140
        $reflectionClass  = new \ReflectionClass($className);
141
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
142
143
        foreach ($classAnnotations as $annotation) {
144
            if (isset(self::$entityAnnotationClasses[get_class($annotation)])) {
145
                return false;
146
            }
147
        }
148
149
        return true;
150
    }
151
152
    /**
153
     * @param array                 $classAnnotations
154
     * @param \ReflectionClass      $reflectionClass
155
     * @param Mapping\ClassMetadata $parent
156
     *
157
     * @return Mapping\ClassMetadata|Mapping\ComponentMetadata
158
     *
159
     * @throws Mapping\MappingException
160
     */
161
    private function convertClassAnnotationsToClassMetadata(
162
        array $classAnnotations,
163
        \ReflectionClass $reflectionClass,
164
        Mapping\ClassMetadata $parent
165
    )
166
    {
167
        switch (true) {
168
            case isset($classAnnotations[Annotation\Entity::class]):
169
                return $this->convertClassAnnotationsToEntityClassMetadata(
170
                    $classAnnotations,
171
                    $reflectionClass,
172
                    $parent
173
                );
174
175
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
176
177
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
178
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
179
                    $classAnnotations,
180
                    $reflectionClass,
181
                    $parent
182
                );
183
184
            case isset($classAnnotations[Annotation\Embeddable::class]):
185
                return $this->convertClassAnnotationsToEntityClassMetadata(
186
                    $classAnnotations,
187
                    $reflectionClass,
188
                    $parent
189
                );
190
191
            default:
192
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
193
        }
194
    }
195
196
    /**
197
     * @param array                 $classAnnotations
198
     * @param \ReflectionClass      $reflectionClass
199
     * @param Mapping\ClassMetadata $parent
200
     *
201
     * @return Mapping\ClassMetadata
202
     *
203
     * @throws Mapping\MappingException
204
     * @throws \UnexpectedValueException
205
     */
206
    private function convertClassAnnotationsToEntityClassMetadata(
207
        array $classAnnotations,
208
        \ReflectionClass $reflectionClass,
209
        Mapping\ClassMetadata $parent
210
    )
211
    {
212
        /** @var Annotation\Entity $entityAnnot */
213
        $entityAnnot  = $classAnnotations[Annotation\Entity::class];
214
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent);
0 ignored issues
show
Bug introduced by
$parent of type Doctrine\ORM\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadataBuildingContext expected by parameter $metadataBuildingContext of Doctrine\ORM\Mapping\ClassMetadata::__construct(). ( Ignorable by Annotation )

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

214
        $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), /** @scrutinizer ignore-type */ $parent);
Loading history...
215
216
        if ($entityAnnot->repositoryClass !== null) {
217
            $classMetadata->setCustomRepositoryClassName(
218
                $classMetadata->fullyQualifiedClassName($entityAnnot->repositoryClass)
219
            );
220
        }
221
222
        if ($entityAnnot->readOnly) {
223
            $classMetadata->asReadOnly();
224
        }
225
226
        // Evaluate @Table annotation
227
        if (isset($classAnnotations[Annotation\Table::class])) {
228
            /** @var Annotation\Table $tableAnnot */
229
            $tableAnnot = $classAnnotations[Annotation\Table::class];
230
            $table      = $this->convertTableAnnotationToTableMetadata($tableAnnot);
231
232
            $classMetadata->setTable($table);
233
        }
234
235
        // Evaluate @ChangeTrackingPolicy annotation
236
        if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) {
237
            /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */
238
            $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class];
239
240
            $classMetadata->setChangeTrackingPolicy(
241
                constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value))
242
            );
243
        }
244
245
        // Evaluate @NamedNativeQueries annotation
246
        if (isset($classAnnotations[Annotation\NamedNativeQueries::class])) {
247
            /** @var Annotation\NamedNativeQueries $namedNativeQueriesAnnot */
248
            $namedNativeQueriesAnnot = $classAnnotations[Annotation\NamedNativeQueries::class];
249
250
            foreach ($namedNativeQueriesAnnot->value as $namedNativeQuery) {
251
                $classMetadata->addNamedNativeQuery(
252
                    $namedNativeQuery->name,
253
                    $namedNativeQuery->query,
254
                    [
255
                        'resultClass'       => $namedNativeQuery->resultClass,
256
                        'resultSetMapping'  => $namedNativeQuery->resultSetMapping,
257
                    ]
258
                );
259
            }
260
        }
261
262
        // Evaluate @SqlResultSetMappings annotation
263
        if (isset($classAnnotations[Annotation\SqlResultSetMappings::class])) {
264
            /** @var Annotation\SqlResultSetMappings $sqlResultSetMappingsAnnot */
265
            $sqlResultSetMappingsAnnot = $classAnnotations[Annotation\SqlResultSetMappings::class];
266
267
            foreach ($sqlResultSetMappingsAnnot->value as $resultSetMapping) {
268
                $sqlResultSetMapping = $this->convertSqlResultSetMapping($resultSetMapping);
269
270
                $classMetadata->addSqlResultSetMapping($sqlResultSetMapping);
271
            }
272
        }
273
274
        // Evaluate @NamedQueries annotation
275
        if (isset($classAnnotations[Annotation\NamedQueries::class])) {
276
            /** @var Annotation\NamedQueries $namedQueriesAnnot */
277
            $namedQueriesAnnot = $classAnnotations[Annotation\NamedQueries::class];
278
279
            if (! is_array($namedQueriesAnnot->value)) {
280
                throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
281
            }
282
283
            foreach ($namedQueriesAnnot->value as $namedQuery) {
284
                if (! ($namedQuery instanceof Annotation\NamedQuery)) {
285
                    throw new \UnexpectedValueException("@NamedQueries should contain an array of @NamedQuery annotations.");
286
                }
287
288
                $classMetadata->addNamedQuery($namedQuery->name, $namedQuery->query);
289
            }
290
        }
291
292
        // Evaluate @EntityListeners annotation
293
        if (isset($classAnnotations[Annotation\EntityListeners::class])) {
294
            /** @var Annotation\EntityListeners $entityListenersAnnot */
295
            $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class];
296
297
            foreach ($entityListenersAnnot->value as $item) {
298
                $listenerClassName = $classMetadata->fullyQualifiedClassName($item);
299
300
                if (! class_exists($listenerClassName)) {
301
                    throw Mapping\MappingException::entityListenerClassNotFound(
302
                        $listenerClassName,
303
                        $reflectionClass->getName()
304
                    );
305
                }
306
307
                $listenerClass = new \ReflectionClass($listenerClassName);
308
309
                /* @var $method \ReflectionMethod */
310
                foreach ($listenerClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
311
                    foreach ($this->getMethodCallbacks($method) as $callback) {
312
                        $classMetadata->addEntityListener($callback, $listenerClassName, $method->getName());
313
                    }
314
                }
315
            }
316
        }
317
318
        // Evaluate @HasLifecycleCallbacks annotation
319
        if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) {
320
            /* @var $method \ReflectionMethod */
321
            foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
322
                foreach ($this->getMethodCallbacks($method) as $callback) {
323
                    $classMetadata->addLifecycleCallback($method->getName(), $callback);
324
                }
325
            }
326
        }
327
328
        // Evaluate @InheritanceType annotation
329
        if (isset($classAnnotations[Annotation\InheritanceType::class])) {
330
            /** @var Annotation\InheritanceType $inheritanceTypeAnnot */
331
            $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class];
332
333
            $classMetadata->setInheritanceType(
334
                constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value))
335
            );
336
337
            if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) {
338
                $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata();
339
340
                // Evaluate @DiscriminatorColumn annotation
341
                if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) {
342
                    /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */
343
                    $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class];
344
345
                    $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name);
346
347
                    if (! empty($discriminatorColumnAnnot->columnDefinition)) {
348
                        $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition);
349
                    }
350
351
                    if (! empty($discriminatorColumnAnnot->type)) {
352
                        $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type));
353
                    }
354
355
                    if (! empty($discriminatorColumnAnnot->length)) {
356
                        $discriminatorColumn->setLength($discriminatorColumnAnnot->length);
357
                    }
358
                }
359
360
                if (empty($discriminatorColumn->getColumnName())) {
361
                    throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName());
362
                }
363
364
                $classMetadata->setDiscriminatorColumn($discriminatorColumn);
365
366
                // Evaluate @DiscriminatorMap annotation
367
                if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) {
368
                    /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */
369
                    $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class];
370
                    $discriminatorMap           = array_map(
371
                        function ($className) use ($classMetadata) {
372
                            return $classMetadata->fullyQualifiedClassName($className);
373
                        },
374
                        $discriminatorMapAnnotation->value
375
                    );
376
377
                    $classMetadata->setDiscriminatorMap($discriminatorMap);
378
                }
379
            }
380
        }
381
382
        return $classMetadata;
383
    }
384
385
    /**
386
     * @param array                 $classAnnotations
387
     * @param \ReflectionClass      $reflectionClass
388
     * @param Mapping\ClassMetadata $parent
389
     *
390
     * @return Mapping\MappedSuperClassMetadata
391
     */
392
    private function convertClassAnnotationsToMappedSuperClassMetadata(
393
        array $classAnnotations,
394
        \ReflectionClass $reflectionClass,
395
        Mapping\ClassMetadata $parent
396
    )
397
    {
398
        /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */
399
        $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class];
400
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), $parent);
0 ignored issues
show
Bug introduced by
$parent of type Doctrine\ORM\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadataBuildingContext expected by parameter $metadataBuildingContext of Doctrine\ORM\Mapping\Map...Metadata::__construct(). ( Ignorable by Annotation )

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

400
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), /** @scrutinizer ignore-type */ $parent);
Loading history...
401
402
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
403
            $classMetadata->setCustomRepositoryClassName(
404
                $classMetadata->fullyQualifiedClassName($mappedSuperclassAnnot->repositoryClass)
405
            );
406
        }
407
408
        return $classMetadata;
409
    }
410
411
    /**
412
     * Parse the given Table as TableMetadata
413
     *
414
     * @param Annotation\Table $tableAnnot
415
     *
416
     * @return Mapping\TableMetadata
417
     */
418
    private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot)
419
    {
420
        $table = new Mapping\TableMetadata();
421
422
        if (! empty($tableAnnot->name)) {
423
            $table->setName($tableAnnot->name);
424
        }
425
426
        if (! empty($tableAnnot->schema)) {
427
            $table->setSchema($tableAnnot->schema);
428
        }
429
430
        foreach ($tableAnnot->options as $optionName => $optionValue) {
431
            $table->addOption($optionName, $optionValue);
432
        }
433
434
        foreach ($tableAnnot->indexes as $indexAnnot) {
435
            $table->addIndex([
436
                'name'    => $indexAnnot->name,
437
                'columns' => $indexAnnot->columns,
438
                'unique'  => $indexAnnot->unique,
439
                'options' => $indexAnnot->options,
440
                'flags'   => $indexAnnot->flags,
441
            ]);
442
        }
443
444
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
445
            $table->addUniqueConstraint([
446
                'name'    => $uniqueConstraintAnnot->name,
447
                'columns' => $uniqueConstraintAnnot->columns,
448
                'options' => $uniqueConstraintAnnot->options,
449
                'flags'   => $uniqueConstraintAnnot->flags,
450
            ]);
451
        }
452
453
        return $table;
454
    }
455
456
    /**
457
     * @param Annotation\SqlResultSetMapping $resultSetMapping
458
     *
459
     * @return array
460
     */
461
    private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping)
462
    {
463
        $entities = [];
464
465
        foreach ($resultSetMapping->entities as $entityResultAnnot) {
466
            $entityResult = [
467
                'fields'                => [],
468
                'entityClass'           => $entityResultAnnot->entityClass,
469
                'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
470
            ];
471
472
            foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
473
                $entityResult['fields'][] = [
474
                    'name'      => $fieldResultAnnot->name,
475
                    'column'    => $fieldResultAnnot->column
476
                ];
477
            }
478
479
            $entities[] = $entityResult;
480
        }
481
482
        $columns = [];
483
484
        foreach ($resultSetMapping->columns as $columnResultAnnot) {
485
            $columns[] = [
486
                'name' => $columnResultAnnot->name,
487
            ];
488
        }
489
490
        return [
491
            'name'     => $resultSetMapping->name,
492
            'entities' => $entities,
493
            'columns'  => $columns
494
        ];
495
    }
496
497
    /**
498
     * Parse the given Cache as CacheMetadata
499
     *
500
     * @param Annotation\Cache      $cacheAnnot
501
     * @param Mapping\ClassMetadata $metadata
502
     * @param null|string           $fieldName
503
     *
504
     * @return Mapping\CacheMetadata
505
     */
506
    private function convertCacheAnnotationToCacheMetadata(
507
        Annotation\Cache $cacheAnnot,
508
        Mapping\ClassMetadata $metadata,
509
        $fieldName = null
510
    )
511
    {
512
        $usage         = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
513
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
514
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
515
516
        return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion);
517
    }
518
519
    /**
520
     * @param \ReflectionProperty   $reflectionProperty
521
     * @param array                 $propertyAnnotations
522
     * @param Mapping\ClassMetadata $classMetadata
523
     *
524
     * @return Mapping\Property
525
     *
526
     * @throws Mapping\MappingException
527
     */
528
    private function convertReflectionPropertyAnnotationsToProperty(
529
        \ReflectionProperty $reflectionProperty,
530
        array $propertyAnnotations,
531
        Mapping\ClassMetadata $classMetadata
532
    )
533
    {
534
        // Field can only be annotated with one of:
535
        // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% 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...
536
        switch (true) {
537
            case isset($propertyAnnotations[Annotation\Column::class]):
538
                return $this->convertReflectionPropertyToFieldMetadata(
539
                    $reflectionProperty,
540
                    $propertyAnnotations,
541
                    $classMetadata
542
                );
543
544
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
545
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
546
                    $reflectionProperty,
547
                    $propertyAnnotations,
548
                    $classMetadata
549
                );
550
551
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
552
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
553
                    $reflectionProperty,
554
                    $propertyAnnotations,
555
                    $classMetadata
556
                );
557
558
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
559
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
560
                    $reflectionProperty,
561
                    $propertyAnnotations,
562
                    $classMetadata
563
                );
564
565
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
566
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
567
                    $reflectionProperty,
568
                    $propertyAnnotations,
569
                    $classMetadata
570
                );
571
572
            case isset($propertyAnnotations[Annotation\Embedded::class]):
573
                // @todo guilhermeblanco Implement later... =)
574
                break;
575
576
            default:
577
                return new Mapping\TransientMetadata($reflectionProperty->getName());
578
        }
579
    }
580
581
    /**
582
     * @param \ReflectionProperty   $reflectionProperty
583
     * @param array                 $propertyAnnotations
584
     * @param Mapping\ClassMetadata $classMetadata
585
     *
586
     * @return Mapping\FieldMetadata
587
     *
588
     * @throws Mapping\MappingException
589
     */
590
    private function convertReflectionPropertyToFieldMetadata(
591
        \ReflectionProperty $reflectionProperty,
592
        array $propertyAnnotations,
593
        Mapping\ClassMetadata $classMetadata
594
    )
595
    {
596
        $className    = $classMetadata->getClassName();
597
        $fieldName    = $reflectionProperty->getName();
598
        $columnAnnot  = $propertyAnnotations[Annotation\Column::class];
599
        $isVersioned  = isset($propertyAnnotations[Annotation\Version::class]);
600
        $isPrimaryKey = isset($propertyAnnotations[Annotation\Id::class]);
601
602
        if ($columnAnnot->type === null) {
603
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
604
        }
605
606
        if ($isVersioned && $isPrimaryKey) {
607
            throw Mapping\MappingException::cannotVersionIdField($className, $fieldName);
608
        }
609
610
        $columnName = empty($columnAnnot->name)
611
            ? $this->namingStrategy->propertyToColumnName($fieldName, $className)
612
            : $columnAnnot->name
613
        ;
614
615
        $fieldMetadata = $isVersioned
616
            ? new Mapping\VersionFieldMetadata($fieldName)
617
            : new Mapping\FieldMetadata($fieldName)
618
        ;
619
620
        $fieldMetadata->setType(Type::getType($columnAnnot->type));
621
        $fieldMetadata->setColumnName($columnName);
622
        $fieldMetadata->setScale($columnAnnot->scale);
623
        $fieldMetadata->setPrecision($columnAnnot->precision);
624
        $fieldMetadata->setNullable($columnAnnot->nullable);
625
        $fieldMetadata->setUnique($columnAnnot->unique);
626
627
        // Check for Id
628
        if ($isPrimaryKey) {
629
            if ($fieldMetadata->getType()->canRequireSQLConversion()) {
630
                throw Mapping\MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($className, $fieldMetadata);
631
            };
632
633
            $fieldMetadata->setPrimaryKey(true);
634
        }
635
636
        if (! empty($columnAnnot->columnDefinition)) {
637
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
638
        }
639
640
        if (! empty($columnAnnot->length)) {
641
            $fieldMetadata->setLength($columnAnnot->length);
642
        }
643
644
        // Assign default options
645
        $customOptions  = $columnAnnot->options ?? [];
646
        $defaultOptions = [];
647
648
        if ($isVersioned) {
649
            switch ($fieldMetadata->getTypeName()) {
650
                case 'integer':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
651
                case 'bigint':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
652
                case 'smallint':
653
                    $defaultOptions['default'] = 1;
654
                    break;
655
656
                case 'datetime':
657
                    $defaultOptions['default'] = 'CURRENT_TIMESTAMP';
658
                    break;
659
660
                default:
661
                    if (! isset($customOptions['default'])) {
662
                        throw Mapping\MappingException::unsupportedOptimisticLockingType($fieldMetadata->getType());
663
                    }
664
            }
665
        }
666
667
        $fieldMetadata->setOptions(array_merge($defaultOptions, $customOptions));
668
669
        return $fieldMetadata;
670
    }
671
672
    /**
673
     * @param \ReflectionProperty   $reflectionProperty
674
     * @param array                 $propertyAnnotations
675
     * @param Mapping\ClassMetadata $classMetadata
676
     *
677
     * @return Mapping\OneToOneAssociationMetadata
678
     */
679
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
680
        \ReflectionProperty $reflectionProperty,
681
        array $propertyAnnotations,
682
        Mapping\ClassMetadata $classMetadata
683
    )
684
    {
685
        $className     = $classMetadata->getClassName();
686
        $fieldName     = $reflectionProperty->getName();
687
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
688
689
        if ($oneToOneAnnot->targetEntity === null) {
690
            throw Mapping\MappingException::missingTargetEntity($fieldName);
691
        }
692
693
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
694
        $targetEntity  = $classMetadata->fullyQualifiedClassName($oneToOneAnnot->targetEntity);
695
696
        $assocMetadata->setSourceEntity($className);
697
        $assocMetadata->setTargetEntity($targetEntity);
698
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
699
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
700
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
701
702
        if (! empty($oneToOneAnnot->mappedBy)) {
703
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
704
        }
705
706
        if (! empty($oneToOneAnnot->inversedBy)) {
707
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
708
        }
709
710
        // Check for Id
711
        if (isset($propertyAnnotations[Annotation\Id::class])) {
712
            $assocMetadata->setPrimaryKey(true);
713
        }
714
715
        // Check for Cache
716 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
717
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
718
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
719
720
            $assocMetadata->setCache($cacheMetadata);
721
        }
722
723
        // Check for JoinColumn/JoinColumns annotations
724 View Code Duplication
        switch (true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
725
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
726
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
727
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
728
                    $reflectionProperty,
729
                    $joinColumnAnnot,
730
                    $classMetadata
731
                );
732
733
                $assocMetadata->addJoinColumn($joinColumn);
734
735
                break;
736
737
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
738
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
739
740
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
741
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
742
                        $reflectionProperty,
743
                        $joinColumnAnnot,
744
                        $classMetadata
745
                    );
746
747
                    $assocMetadata->addJoinColumn($joinColumn);
748
                }
749
750
                break;
751
        }
752
753
        return $assocMetadata;
754
    }
755
756
    /**
757
     * @param \ReflectionProperty   $reflectionProperty
758
     * @param array                 $propertyAnnotations
759
     * @param Mapping\ClassMetadata $classMetadata
760
     *
761
     * @return Mapping\ManyToOneAssociationMetadata
762
     */
763
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
764
        \ReflectionProperty $reflectionProperty,
765
        array $propertyAnnotations,
766
        Mapping\ClassMetadata $classMetadata
767
    )
768
    {
769
        $className      = $classMetadata->getClassName();
770
        $fieldName      = $reflectionProperty->getName();
771
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
772
773
        if ($manyToOneAnnot->targetEntity === null) {
774
            throw Mapping\MappingException::missingTargetEntity($fieldName);
775
        }
776
777
        $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName);
778
        $targetEntity  = $classMetadata->fullyQualifiedClassName($manyToOneAnnot->targetEntity);
779
780
        $assocMetadata->setSourceEntity($className);
781
        $assocMetadata->setTargetEntity($targetEntity);
782
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
783
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
784
785
        if (! empty($manyToOneAnnot->inversedBy)) {
786
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
787
        }
788
789
        // Check for Id
790
        if (isset($propertyAnnotations[Annotation\Id::class])) {
791
            $assocMetadata->setPrimaryKey(true);
792
        }
793
794
        // Check for Cache
795 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
796
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
797
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
798
799
            $assocMetadata->setCache($cacheMetadata);
800
        }
801
802
        // Check for JoinColumn/JoinColumns annotations
803 View Code Duplication
        switch (true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
804
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
805
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
806
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
807
                    $reflectionProperty,
808
                    $joinColumnAnnot,
809
                    $classMetadata
810
                );
811
812
                $assocMetadata->addJoinColumn($joinColumn);
813
814
                break;
815
816
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
817
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
818
819
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
820
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
821
                        $reflectionProperty,
822
                        $joinColumnAnnot,
823
                        $classMetadata
824
                    );
825
826
                    $assocMetadata->addJoinColumn($joinColumn);
827
                }
828
829
                break;
830
        }
831
832
        return $assocMetadata;
833
    }
834
835
    /**
836
     * @param \ReflectionProperty   $reflectionProperty
837
     * @param array                 $propertyAnnotations
838
     * @param Mapping\ClassMetadata $classMetadata
839
     *
840
     * @return Mapping\OneToManyAssociationMetadata
841
     */
842
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
843
        \ReflectionProperty $reflectionProperty,
844
        array $propertyAnnotations,
845
        Mapping\ClassMetadata $classMetadata
846
    )
847
    {
848
        $className      = $classMetadata->getClassName();
849
        $fieldName      = $reflectionProperty->getName();
850
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
851
852
        if ($oneToManyAnnot->targetEntity === null) {
853
            throw Mapping\MappingException::missingTargetEntity($fieldName);
854
        }
855
856
        $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName);
857
        $targetEntity  = $classMetadata->fullyQualifiedClassName($oneToManyAnnot->targetEntity);
858
859
        $assocMetadata->setSourceEntity($className);
860
        $assocMetadata->setTargetEntity($targetEntity);
861
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
862
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
863
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
864
865
        if (! empty($oneToManyAnnot->mappedBy)) {
866
            $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
867
        }
868
869
        if (! empty($oneToManyAnnot->indexBy)) {
870
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
871
        }
872
873
        // Check for OrderBy
874 View Code Duplication
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
875
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
876
877
            $assocMetadata->setOrderBy($orderByAnnot->value);
878
        }
879
880
        // Check for Id
881
        if (isset($propertyAnnotations[Annotation\Id::class])) {
882
            $assocMetadata->setPrimaryKey(true);
883
        }
884
885
        // Check for Cache
886 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
887
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
888
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
889
890
            $assocMetadata->setCache($cacheMetadata);
891
        }
892
893
        return $assocMetadata;
894
    }
895
896
    /**
897
     * @param \ReflectionProperty   $reflectionProperty
898
     * @param array                 $propertyAnnotations
899
     * @param Mapping\ClassMetadata $classMetadata
900
     *
901
     * @return Mapping\ManyToManyAssociationMetadata
902
     */
903
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
904
        \ReflectionProperty $reflectionProperty,
905
        array $propertyAnnotations,
906
        Mapping\ClassMetadata $classMetadata
907
    )
908
    {
909
        $className       = $classMetadata->getClassName();
910
        $fieldName       = $reflectionProperty->getName();
911
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
912
913
        if ($manyToManyAnnot->targetEntity === null) {
914
            throw Mapping\MappingException::missingTargetEntity($fieldName);
915
        }
916
917
        $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName);
918
        $targetEntity  = $classMetadata->fullyQualifiedClassName($manyToManyAnnot->targetEntity);
919
920
        $assocMetadata->setSourceEntity($className);
921
        $assocMetadata->setTargetEntity($targetEntity);
922
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
923
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
924
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
925
926
        if (! empty($manyToManyAnnot->mappedBy)) {
927
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
928
        }
929
930
        if (! empty($manyToManyAnnot->inversedBy)) {
931
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
932
        }
933
934
        if (! empty($manyToManyAnnot->indexBy)) {
935
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
936
        }
937
938
        // Check for JoinTable
939
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
940
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
941
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata(
942
                $reflectionProperty,
943
                $joinTableAnnot,
944
                $classMetadata
945
            );
946
947
            $assocMetadata->setJoinTable($joinTableMetadata);
948
        }
949
950
        // Check for OrderBy
951 View Code Duplication
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
952
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
953
954
            $assocMetadata->setOrderBy($orderByAnnot->value);
955
        }
956
957
        // Check for Id
958
        if (isset($propertyAnnotations[Annotation\Id::class])) {
959
            $assocMetadata->setPrimaryKey(true);
960
        }
961
962
        // Check for Cache
963 View Code Duplication
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
964
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
965
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
966
967
            $assocMetadata->setCache($cacheMetadata);
968
        }
969
970
        return $assocMetadata;
971
    }
972
973
    /**
974
     * Parse the given JoinTable as JoinTableMetadata
975
     *
976
     * @param \ReflectionProperty   $reflectionProperty
977
     * @param Annotation\JoinTable  $joinTableAnnot
978
     * @param Mapping\ClassMetadata $classMetadata
979
     *
980
     * @return Mapping\JoinTableMetadata
981
     */
982
    private function convertJoinTableAnnotationToJoinTableMetadata(
983
        \ReflectionProperty $reflectionProperty,
984
        Annotation\JoinTable $joinTableAnnot,
985
        Mapping\ClassMetadata $classMetadata
986
    )
987
    {
988
        $joinTable = new Mapping\JoinTableMetadata();
989
990
        if (! empty($joinTableAnnot->name)) {
991
            $joinTable->setName($joinTableAnnot->name);
992
        }
993
994
        if (! empty($joinTableAnnot->schema)) {
995
            $joinTable->setSchema($joinTableAnnot->schema);
996
        }
997
998
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
999
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
1000
                $reflectionProperty,
1001
                $joinColumnAnnot,
1002
                $classMetadata
1003
            );
1004
1005
            $joinTable->addJoinColumn($joinColumn);
1006
        }
1007
1008
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
1009
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
1010
                $reflectionProperty,
1011
                $joinColumnAnnot,
1012
                $classMetadata
1013
            );
1014
1015
            $joinTable->addInverseJoinColumn($joinColumn);
1016
        }
1017
1018
        return $joinTable;
1019
    }
1020
1021
    /**
1022
     * Parse the given JoinColumn as JoinColumnMetadata
1023
     *
1024
     * @param Annotation\JoinColumn $joinColumnAnnot
1025
     *
1026
     * @return Mapping\JoinColumnMetadata
1027
     */
1028
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
1029
        \ReflectionProperty $reflectionProperty,
1030
        Annotation\JoinColumn $joinColumnAnnot,
1031
        Mapping\ClassMetadata $classMetadata
1032
    )
1033
    {
1034
        $fieldName  = $reflectionProperty->getName();
1035
        $joinColumn = new Mapping\JoinColumnMetadata();
1036
        $columnName = empty($joinColumnAnnot->name)
1037
            ? $this->namingStrategy->propertyToColumnName($fieldName, $classMetadata->getClassName())
1038
            : $joinColumnAnnot->name
1039
        ;
1040
        $referencedColumnName = empty($joinColumnAnnot->referencedColumnName)
1041
            ? $this->namingStrategy->referenceColumnName()
1042
            : $joinColumnAnnot->referencedColumnName
1043
        ;
1044
1045
        $joinColumn->setColumnName($columnName);
1046
        $joinColumn->setReferencedColumnName($referencedColumnName);
1047
        $joinColumn->setNullable($joinColumnAnnot->nullable);
1048
        $joinColumn->setUnique($joinColumnAnnot->unique);
1049
1050
        if (! empty($joinColumnAnnot->fieldName)) {
1051
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
1052
        }
1053
1054
        if (! empty($joinColumnAnnot->columnDefinition)) {
1055
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
1056
        }
1057
1058
        if ($joinColumnAnnot->onDelete) {
1059
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
1060
        }
1061
1062
        return $joinColumn;
1063
    }
1064
1065
    /**
1066
     * Parses the given method.
1067
     *
1068
     * @param \ReflectionMethod $method
1069
     *
1070
     * @return array
1071
     */
1072
    private function getMethodCallbacks(\ReflectionMethod $method)
1073
    {
1074
        $annotations = $this->getMethodAnnotations($method);
1075
        $events      = [
1076
            Events::prePersist  => Annotation\PrePersist::class,
1077
            Events::postPersist => Annotation\PostPersist::class,
1078
            Events::preUpdate   => Annotation\PreUpdate::class,
1079
            Events::postUpdate  => Annotation\PostUpdate::class,
1080
            Events::preRemove   => Annotation\PreRemove::class,
1081
            Events::postRemove  => Annotation\PostRemove::class,
1082
            Events::postLoad    => Annotation\PostLoad::class,
1083
            Events::preFlush    => Annotation\PreFlush::class,
1084
        ];
1085
1086
        // Check for callbacks
1087
        $callbacks = [];
1088
1089
        foreach ($events as $eventName => $annotationClassName) {
1090
            if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) {
1091
                $callbacks[] = $eventName;
1092
            }
1093
        }
1094
1095
        return $callbacks;
1096
    }
1097
1098
    /**
1099
     * Attempts to resolve the fetch mode.
1100
     *
1101
     * @param string $className The class name.
1102
     * @param string $fetchMode The fetch mode.
1103
     *
1104
     * @return integer The fetch mode as defined in ClassMetadata.
1105
     *
1106
     * @throws Mapping\MappingException If the fetch mode is not valid.
1107
     */
1108
    private function getFetchMode($className, $fetchMode)
1109
    {
1110
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1111
1112
        if (! defined($fetchModeConstant)) {
1113
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1114
        }
1115
1116
        return constant($fetchModeConstant);
1117
    }
1118
1119
    /**
1120
     * @param string $className        The class name.
1121
     * @param string $fieldName        The field name.
1122
     * @param array  $originalCascades The original unprocessed field cascades.
1123
     *
1124
     * @return array The processed field cascades.
1125
     *
1126
     * @throws Mapping\MappingException If a cascade option is not valid.
1127
     */
1128
    private function getCascade(string $className, string $fieldName, array $originalCascades)
1129
    {
1130
        $cascadeTypes = ['remove', 'persist', 'refresh'];
1131
        $cascades     = array_map('strtolower', $originalCascades);
1132
1133
        if (in_array('all', $cascades)) {
1134
            $cascades = $cascadeTypes;
1135
        }
1136
1137
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1138
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1139
1140
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1141
        }
1142
1143
        return $cascades;
1144
    }
1145
1146
    /**
1147
     * @param \ReflectionClass $reflectionClass
1148
     *
1149
     * @return array
1150
     */
1151 View Code Duplication
    private function getClassAnnotations(\ReflectionClass $reflectionClass)
1152
    {
1153
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1154
1155
        foreach ($classAnnotations as $key => $annot) {
1156
            if (! is_numeric($key)) {
1157
                continue;
1158
            }
1159
1160
            $classAnnotations[get_class($annot)] = $annot;
1161
        }
1162
1163
        return $classAnnotations;
1164
    }
1165
1166
    /**
1167
     * @param \ReflectionProperty $reflectionProperty
1168
     *
1169
     * @return array
1170
     */
1171 View Code Duplication
    private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty)
1172
    {
1173
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1174
1175
        foreach ($propertyAnnotations as $key => $annot) {
1176
            if (! is_numeric($key)) {
1177
                continue;
1178
            }
1179
1180
            $propertyAnnotations[get_class($annot)] = $annot;
1181
        }
1182
1183
        return $propertyAnnotations;
1184
    }
1185
1186
    /**
1187
     * @param \ReflectionMethod $reflectionMethod
1188
     *
1189
     * @return array
1190
     */
1191 View Code Duplication
    private function getMethodAnnotations(\ReflectionMethod $reflectionMethod)
1192
    {
1193
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1194
1195
        foreach ($methodAnnotations as $key => $annot) {
1196
            if (! is_numeric($key)) {
1197
                continue;
1198
            }
1199
1200
            $methodAnnotations[get_class($annot)] = $annot;
1201
        }
1202
1203
        return $methodAnnotations;
1204
    }
1205
}
1206