Failed Conditions
Push — master ( 8be1e3...e3936d )
by Marco
14s
created

convertReflectionPropertyToOneToManyAssociationMetadata()   C

Complexity

Conditions 7
Paths 33

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 33
nop 3
dl 0
loc 51
ccs 0
cts 37
cp 0
crap 56
rs 6.9743
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
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
    /** @var int[] */
18
    static protected $entityAnnotationClasses = [
19
        Annotation\Entity::class           => 1,
20
        Annotation\MappedSuperclass::class => 2,
21
    ];
22
23
    /**
24
     * The Annotation reader.
25
     *
26
     * @var AnnotationReader
27
     */
28
    protected $reader;
29
30
    /**
31
     * The file locator.
32
     *
33
     * @var FileLocator
34
     */
35
    protected $locator;
36
37
    /**
38
     * @var Factory\NamingStrategy
39
     */
40
    protected $namingStrategy;
41
42
    /**
43
     * Cache for AnnotationDriver#getAllClassNames().
44
     *
45
     * @var string[]|null
46
     */
47
    private $classNames;
48
49
    /**
50
     * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations.
51
     *
52
     * @param AnnotationReader       $reader         The AnnotationReader to use, duck-typed.
53
     * @param FileLocator            $locator        A FileLocator or one/multiple paths where mapping documents can be found.
54
     * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use.
55
     */
56
    public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy)
57
    {
58
        $this->reader         = $reader;
59
        $this->locator        = $locator;
60
        $this->namingStrategy = $namingStrategy;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     *
66
     * @return Mapping\ClassMetadata
67
     *
68
     * @throws Mapping\MappingException
69
     */
70
    public function loadMetadataForClass(
71
        string $className,
72
        Mapping\ClassMetadata $metadata,
73
        Mapping\ClassMetadataBuildingContext $metadataBuildingContext
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
        if (isset($classAnnotations[Annotation\Cache::class])) {
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 Annotation\Annotation[] $classAnnotations
154
     *
155
     * @return Mapping\ClassMetadata|Mapping\ComponentMetadata
156
     *
157
     * @throws Mapping\MappingException
158
     */
159
    private function convertClassAnnotationsToClassMetadata(
160
        array $classAnnotations,
161
        \ReflectionClass $reflectionClass,
162
        Mapping\ClassMetadata $parent
163
    ) {
164
        switch (true) {
165
            case isset($classAnnotations[Annotation\Entity::class]):
166
                return $this->convertClassAnnotationsToEntityClassMetadata(
167
                    $classAnnotations,
168
                    $reflectionClass,
169
                    $parent
170
                );
171
172
                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...
173
174
            case isset($classAnnotations[Annotation\MappedSuperclass::class]):
175
                return $this->convertClassAnnotationsToMappedSuperClassMetadata(
176
                    $classAnnotations,
177
                    $reflectionClass,
178
                    $parent
179
                );
180
181
            case isset($classAnnotations[Annotation\Embeddable::class]):
182
                return $this->convertClassAnnotationsToEntityClassMetadata(
183
                    $classAnnotations,
184
                    $reflectionClass,
185
                    $parent
186
                );
187
188
            default:
189
                throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName());
190
        }
191
    }
192
193
    /**
194
     * @param Annotation\Annotation[] $classAnnotations
195
     *
196
     * @return Mapping\ClassMetadata
197
     *
198
     * @throws Mapping\MappingException
199
     * @throws \UnexpectedValueException
200
     */
201
    private function convertClassAnnotationsToEntityClassMetadata(
202
        array $classAnnotations,
203
        \ReflectionClass $reflectionClass,
204
        Mapping\ClassMetadata $parent
205
    ) {
206
        /** @var Annotation\Entity $entityAnnot */
207
        $entityAnnot   = $classAnnotations[Annotation\Entity::class];
208
        $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

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

382
        $classMetadata         = new Mapping\MappedSuperClassMetadata($reflectionClass->getName(), /** @scrutinizer ignore-type */ $parent);
Loading history...
383
384
        if ($mappedSuperclassAnnot->repositoryClass !== null) {
385
            $classMetadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass);
386
        }
387
388
        return $classMetadata;
389
    }
390
391
    /**
392
     * Parse the given Table as TableMetadata
393
     *
394
     * @return Mapping\TableMetadata
395
     */
396
    private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot)
397
    {
398
        $table = new Mapping\TableMetadata();
399
400
        if (! empty($tableAnnot->name)) {
401
            $table->setName($tableAnnot->name);
402
        }
403
404
        if (! empty($tableAnnot->schema)) {
405
            $table->setSchema($tableAnnot->schema);
406
        }
407
408
        foreach ($tableAnnot->options as $optionName => $optionValue) {
409
            $table->addOption($optionName, $optionValue);
410
        }
411
412
        foreach ($tableAnnot->indexes as $indexAnnot) {
413
            $table->addIndex([
414
                'name'    => $indexAnnot->name,
415
                'columns' => $indexAnnot->columns,
416
                'unique'  => $indexAnnot->unique,
417
                'options' => $indexAnnot->options,
418
                'flags'   => $indexAnnot->flags,
419
            ]);
420
        }
421
422
        foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) {
423
            $table->addUniqueConstraint([
424
                'name'    => $uniqueConstraintAnnot->name,
425
                'columns' => $uniqueConstraintAnnot->columns,
426
                'options' => $uniqueConstraintAnnot->options,
427
                'flags'   => $uniqueConstraintAnnot->flags,
428
            ]);
429
        }
430
431
        return $table;
432
    }
433
434
    /**
435
     * @return mixed[]
436
     */
437
    private function convertSqlResultSetMapping(Annotation\SqlResultSetMapping $resultSetMapping)
438
    {
439
        $entities = [];
440
441
        foreach ($resultSetMapping->entities as $entityResultAnnot) {
442
            $entityResult = [
443
                'fields'                => [],
444
                'entityClass'           => $entityResultAnnot->entityClass,
445
                'discriminatorColumn'   => $entityResultAnnot->discriminatorColumn,
446
            ];
447
448
            foreach ($entityResultAnnot->fields as $fieldResultAnnot) {
449
                $entityResult['fields'][] = [
450
                    'name'      => $fieldResultAnnot->name,
451
                    'column'    => $fieldResultAnnot->column,
452
                ];
453
            }
454
455
            $entities[] = $entityResult;
456
        }
457
458
        $columns = [];
459
460
        foreach ($resultSetMapping->columns as $columnResultAnnot) {
461
            $columns[] = [
462
                'name' => $columnResultAnnot->name,
463
            ];
464
        }
465
466
        return [
467
            'name'     => $resultSetMapping->name,
468
            'entities' => $entities,
469
            'columns'  => $columns,
470
        ];
471
    }
472
473
    /**
474
     * Parse the given Cache as CacheMetadata
475
     *
476
     * @param string|null $fieldName
477
     *
478
     * @return Mapping\CacheMetadata
479
     */
480
    private function convertCacheAnnotationToCacheMetadata(
481
        Annotation\Cache $cacheAnnot,
482
        Mapping\ClassMetadata $metadata,
483
        $fieldName = null
484
    ) {
485
        $usage         = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage));
486
        $baseRegion    = strtolower(str_replace('\\', '_', $metadata->getRootClassName()));
487
        $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : '');
488
489
        return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion);
490
    }
491
492
    /**
493
     * @param Annotation\Annotation[] $propertyAnnotations
494
     *
495
     * @return Mapping\Property
496
     *
497
     * @throws Mapping\MappingException
498
     */
499
    private function convertReflectionPropertyAnnotationsToProperty(
500
        \ReflectionProperty $reflectionProperty,
501
        array $propertyAnnotations,
502
        Mapping\ClassMetadata $classMetadata
503
    ) {
504
        // Field can only be annotated with one of:
505
        // @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...
506
        switch (true) {
507
            case isset($propertyAnnotations[Annotation\Column::class]):
508
                return $this->convertReflectionPropertyToFieldMetadata(
509
                    $reflectionProperty,
510
                    $propertyAnnotations,
511
                    $classMetadata
512
                );
513
514
            case isset($propertyAnnotations[Annotation\OneToOne::class]):
515
                return $this->convertReflectionPropertyToOneToOneAssociationMetadata(
516
                    $reflectionProperty,
517
                    $propertyAnnotations,
518
                    $classMetadata
519
                );
520
521
            case isset($propertyAnnotations[Annotation\ManyToOne::class]):
522
                return $this->convertReflectionPropertyToManyToOneAssociationMetadata(
523
                    $reflectionProperty,
524
                    $propertyAnnotations,
525
                    $classMetadata
526
                );
527
528
            case isset($propertyAnnotations[Annotation\OneToMany::class]):
529
                return $this->convertReflectionPropertyToOneToManyAssociationMetadata(
530
                    $reflectionProperty,
531
                    $propertyAnnotations,
532
                    $classMetadata
533
                );
534
535
            case isset($propertyAnnotations[Annotation\ManyToMany::class]):
536
                return $this->convertReflectionPropertyToManyToManyAssociationMetadata(
537
                    $reflectionProperty,
538
                    $propertyAnnotations,
539
                    $classMetadata
540
                );
541
542
            case isset($propertyAnnotations[Annotation\Embedded::class]):
543
                // @todo guilhermeblanco Implement later... =)
544
                break;
545
546
            default:
547
                return new Mapping\TransientMetadata($reflectionProperty->getName());
548
        }
549
    }
550
551
    /**
552
     * @param Annotation\Annotation[] $propertyAnnotations
553
     *
554
     * @return Mapping\FieldMetadata
555
     *
556
     * @throws Mapping\MappingException
557
     */
558
    private function convertReflectionPropertyToFieldMetadata(
559
        \ReflectionProperty $reflectionProperty,
560
        array $propertyAnnotations,
561
        Mapping\ClassMetadata $classMetadata
562
    ) {
563
        $className    = $classMetadata->getClassName();
564
        $fieldName    = $reflectionProperty->getName();
565
        $columnAnnot  = $propertyAnnotations[Annotation\Column::class];
566
        $isVersioned  = isset($propertyAnnotations[Annotation\Version::class]);
567
        $isPrimaryKey = isset($propertyAnnotations[Annotation\Id::class]);
568
569
        if ($columnAnnot->type === null) {
0 ignored issues
show
Bug introduced by
Accessing type on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
570
            throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName);
571
        }
572
573
        if ($isVersioned && $isPrimaryKey) {
574
            throw Mapping\MappingException::cannotVersionIdField($className, $fieldName);
575
        }
576
577
        $columnName = empty($columnAnnot->name)
0 ignored issues
show
Bug introduced by
Accessing name on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
578
            ? $this->namingStrategy->propertyToColumnName($fieldName, $className)
579
            : $columnAnnot->name
580
        ;
581
582
        $fieldMetadata = $isVersioned
583
            ? new Mapping\VersionFieldMetadata($fieldName)
584
            : new Mapping\FieldMetadata($fieldName)
585
        ;
586
587
        $fieldMetadata->setType(Type::getType($columnAnnot->type));
588
        $fieldMetadata->setColumnName($columnName);
589
        $fieldMetadata->setScale($columnAnnot->scale);
0 ignored issues
show
Bug introduced by
Accessing scale on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
590
        $fieldMetadata->setPrecision($columnAnnot->precision);
0 ignored issues
show
Bug introduced by
Accessing precision on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
591
        $fieldMetadata->setNullable($columnAnnot->nullable);
0 ignored issues
show
Bug introduced by
Accessing nullable on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
592
        $fieldMetadata->setUnique($columnAnnot->unique);
0 ignored issues
show
Bug introduced by
Accessing unique on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
593
594
        // Check for Id
595
        if ($isPrimaryKey) {
596
            if ($fieldMetadata->getType()->canRequireSQLConversion()) {
597
                throw Mapping\MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($className, $fieldMetadata);
598
            }
599
600
            $fieldMetadata->setPrimaryKey(true);
601
        }
602
603
        if (! empty($columnAnnot->columnDefinition)) {
0 ignored issues
show
Bug introduced by
Accessing columnDefinition on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
604
            $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition);
605
        }
606
607
        if (! empty($columnAnnot->length)) {
0 ignored issues
show
Bug introduced by
Accessing length on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
608
            $fieldMetadata->setLength($columnAnnot->length);
609
        }
610
611
        // Assign default options
612
        $customOptions  = $columnAnnot->options ?? [];
0 ignored issues
show
Bug introduced by
Accessing options on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
613
        $defaultOptions = [];
614
615
        if ($isVersioned) {
616
            switch ($fieldMetadata->getTypeName()) {
617
                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...
618
                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...
619
                case 'smallint':
620
                    $defaultOptions['default'] = 1;
621
                    break;
622
623
                case 'datetime':
624
                    $defaultOptions['default'] = 'CURRENT_TIMESTAMP';
625
                    break;
626
627
                default:
628
                    if (! isset($customOptions['default'])) {
629
                        throw Mapping\MappingException::unsupportedOptimisticLockingType($fieldMetadata->getType());
630
                    }
631
            }
632
        }
633
634
        $fieldMetadata->setOptions(array_merge($defaultOptions, $customOptions));
635
636
        return $fieldMetadata;
637
    }
638
639
    /**
640
     * @param Annotation\Annotation[] $propertyAnnotations
641
     *
642
     * @return Mapping\OneToOneAssociationMetadata
643
     */
644
    private function convertReflectionPropertyToOneToOneAssociationMetadata(
645
        \ReflectionProperty $reflectionProperty,
646
        array $propertyAnnotations,
647
        Mapping\ClassMetadata $classMetadata
648
    ) {
649
        $className     = $classMetadata->getClassName();
650
        $fieldName     = $reflectionProperty->getName();
651
        $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class];
652
653
        if ($oneToOneAnnot->targetEntity === null) {
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
654
            throw Mapping\MappingException::missingTargetEntity($fieldName);
655
        }
656
657
        $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName);
658
        $targetEntity  = $oneToOneAnnot->targetEntity;
659
660
        $assocMetadata->setSourceEntity($className);
661
        $assocMetadata->setTargetEntity($targetEntity);
662
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
663
        $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
664
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
665
666
        if (! empty($oneToOneAnnot->mappedBy)) {
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
667
            $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy);
668
        }
669
670
        if (! empty($oneToOneAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
671
            $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy);
672
        }
673
674
        // Check for Id
675
        if (isset($propertyAnnotations[Annotation\Id::class])) {
676
            $assocMetadata->setPrimaryKey(true);
677
        }
678
679
        // Check for Cache
680
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
681
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
682
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
683
684
            $assocMetadata->setCache($cacheMetadata);
685
        }
686
687
        // Check for JoinColumn/JoinColumns annotations
688
        switch (true) {
689
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
690
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
691
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
692
                    $reflectionProperty,
693
                    $joinColumnAnnot,
694
                    $classMetadata
695
                );
696
697
                $assocMetadata->addJoinColumn($joinColumn);
698
699
                break;
700
701
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
702
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
703
704
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
705
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
706
                        $reflectionProperty,
707
                        $joinColumnAnnot,
708
                        $classMetadata
709
                    );
710
711
                    $assocMetadata->addJoinColumn($joinColumn);
712
                }
713
714
                break;
715
        }
716
717
        return $assocMetadata;
718
    }
719
720
    /**
721
     * @param Annotation\Annotation[] $propertyAnnotations
722
     *
723
     * @return Mapping\ManyToOneAssociationMetadata
724
     */
725
    private function convertReflectionPropertyToManyToOneAssociationMetadata(
726
        \ReflectionProperty $reflectionProperty,
727
        array $propertyAnnotations,
728
        Mapping\ClassMetadata $classMetadata
729
    ) {
730
        $className      = $classMetadata->getClassName();
731
        $fieldName      = $reflectionProperty->getName();
732
        $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class];
733
734
        if ($manyToOneAnnot->targetEntity === null) {
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
735
            throw Mapping\MappingException::missingTargetEntity($fieldName);
736
        }
737
738
        $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName);
739
        $targetEntity  = $manyToOneAnnot->targetEntity;
740
741
        $assocMetadata->setSourceEntity($className);
742
        $assocMetadata->setTargetEntity($targetEntity);
743
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
744
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
745
746
        if (! empty($manyToOneAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
747
            $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy);
748
        }
749
750
        // Check for Id
751
        if (isset($propertyAnnotations[Annotation\Id::class])) {
752
            $assocMetadata->setPrimaryKey(true);
753
        }
754
755
        // Check for Cache
756
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
757
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
758
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
759
760
            $assocMetadata->setCache($cacheMetadata);
761
        }
762
763
        // Check for JoinColumn/JoinColumns annotations
764
        switch (true) {
765
            case isset($propertyAnnotations[Annotation\JoinColumn::class]):
766
                $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class];
767
                $joinColumn      = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
768
                    $reflectionProperty,
769
                    $joinColumnAnnot,
770
                    $classMetadata
771
                );
772
773
                $assocMetadata->addJoinColumn($joinColumn);
774
775
                break;
776
777
            case isset($propertyAnnotations[Annotation\JoinColumns::class]):
778
                $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class];
779
780
                foreach ($joinColumnsAnnot->value as $joinColumnAnnot) {
781
                    $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
782
                        $reflectionProperty,
783
                        $joinColumnAnnot,
784
                        $classMetadata
785
                    );
786
787
                    $assocMetadata->addJoinColumn($joinColumn);
788
                }
789
790
                break;
791
        }
792
793
        return $assocMetadata;
794
    }
795
796
    /**
797
     * @param Annotation\Annotation[] $propertyAnnotations
798
     *
799
     * @return Mapping\OneToManyAssociationMetadata
800
     */
801
    private function convertReflectionPropertyToOneToManyAssociationMetadata(
802
        \ReflectionProperty $reflectionProperty,
803
        array $propertyAnnotations,
804
        Mapping\ClassMetadata $classMetadata
805
    ) {
806
        $className      = $classMetadata->getClassName();
807
        $fieldName      = $reflectionProperty->getName();
808
        $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class];
809
810
        if ($oneToManyAnnot->targetEntity === null) {
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
811
            throw Mapping\MappingException::missingTargetEntity($fieldName);
812
        }
813
814
        $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName);
815
        $targetEntity  = $oneToManyAnnot->targetEntity;
816
817
        $assocMetadata->setSourceEntity($className);
818
        $assocMetadata->setTargetEntity($targetEntity);
819
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
820
        $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
821
        $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
822
823
        if (! empty($oneToManyAnnot->mappedBy)) {
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
824
            $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy);
825
        }
826
827
        if (! empty($oneToManyAnnot->indexBy)) {
0 ignored issues
show
Bug introduced by
Accessing indexBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
828
            $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy);
829
        }
830
831
        // Check for OrderBy
832
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
833
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
834
835
            $assocMetadata->setOrderBy($orderByAnnot->value);
0 ignored issues
show
Bug introduced by
Accessing value on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
836
        }
837
838
        // Check for Id
839
        if (isset($propertyAnnotations[Annotation\Id::class])) {
840
            $assocMetadata->setPrimaryKey(true);
841
        }
842
843
        // Check for Cache
844
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
845
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
846
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
847
848
            $assocMetadata->setCache($cacheMetadata);
849
        }
850
851
        return $assocMetadata;
852
    }
853
854
    /**
855
     * @param Annotation\Annotation[] $propertyAnnotations
856
     *
857
     * @return Mapping\ManyToManyAssociationMetadata
858
     */
859
    private function convertReflectionPropertyToManyToManyAssociationMetadata(
860
        \ReflectionProperty $reflectionProperty,
861
        array $propertyAnnotations,
862
        Mapping\ClassMetadata $classMetadata
863
    ) {
864
        $className       = $classMetadata->getClassName();
865
        $fieldName       = $reflectionProperty->getName();
866
        $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class];
867
868
        if ($manyToManyAnnot->targetEntity === null) {
0 ignored issues
show
Bug introduced by
Accessing targetEntity on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
869
            throw Mapping\MappingException::missingTargetEntity($fieldName);
870
        }
871
872
        $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName);
873
        $targetEntity  = $manyToManyAnnot->targetEntity;
874
875
        $assocMetadata->setSourceEntity($className);
876
        $assocMetadata->setTargetEntity($targetEntity);
877
        $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade));
0 ignored issues
show
Bug introduced by
Accessing cascade on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
878
        $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval);
0 ignored issues
show
Bug introduced by
Accessing orphanRemoval on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
879
        $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch));
0 ignored issues
show
Bug introduced by
Accessing fetch on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
880
881
        if (! empty($manyToManyAnnot->mappedBy)) {
0 ignored issues
show
Bug introduced by
Accessing mappedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
882
            $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy);
883
        }
884
885
        if (! empty($manyToManyAnnot->inversedBy)) {
0 ignored issues
show
Bug introduced by
Accessing inversedBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
886
            $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy);
887
        }
888
889
        if (! empty($manyToManyAnnot->indexBy)) {
0 ignored issues
show
Bug introduced by
Accessing indexBy on the interface Doctrine\ORM\Annotation\Annotation suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
890
            $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy);
891
        }
892
893
        // Check for JoinTable
894
        if (isset($propertyAnnotations[Annotation\JoinTable::class])) {
895
            $joinTableAnnot    = $propertyAnnotations[Annotation\JoinTable::class];
896
            $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata(
897
                $reflectionProperty,
898
                $joinTableAnnot,
899
                $classMetadata
900
            );
901
902
            $assocMetadata->setJoinTable($joinTableMetadata);
903
        }
904
905
        // Check for OrderBy
906
        if (isset($propertyAnnotations[Annotation\OrderBy::class])) {
907
            $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class];
908
909
            $assocMetadata->setOrderBy($orderByAnnot->value);
910
        }
911
912
        // Check for Id
913
        if (isset($propertyAnnotations[Annotation\Id::class])) {
914
            $assocMetadata->setPrimaryKey(true);
915
        }
916
917
        // Check for Cache
918
        if (isset($propertyAnnotations[Annotation\Cache::class])) {
919
            $cacheAnnot    = $propertyAnnotations[Annotation\Cache::class];
920
            $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName);
921
922
            $assocMetadata->setCache($cacheMetadata);
923
        }
924
925
        return $assocMetadata;
926
    }
927
928
    /**
929
     * Parse the given JoinTable as JoinTableMetadata
930
     *
931
     * @return Mapping\JoinTableMetadata
932
     */
933
    private function convertJoinTableAnnotationToJoinTableMetadata(
934
        \ReflectionProperty $reflectionProperty,
935
        Annotation\JoinTable $joinTableAnnot,
936
        Mapping\ClassMetadata $classMetadata
937
    ) {
938
        $joinTable = new Mapping\JoinTableMetadata();
939
940
        if (! empty($joinTableAnnot->name)) {
941
            $joinTable->setName($joinTableAnnot->name);
942
        }
943
944
        if (! empty($joinTableAnnot->schema)) {
945
            $joinTable->setSchema($joinTableAnnot->schema);
946
        }
947
948
        foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) {
949
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
950
                $reflectionProperty,
951
                $joinColumnAnnot,
952
                $classMetadata
953
            );
954
955
            $joinTable->addJoinColumn($joinColumn);
956
        }
957
958
        foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) {
959
            $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata(
960
                $reflectionProperty,
961
                $joinColumnAnnot,
962
                $classMetadata
963
            );
964
965
            $joinTable->addInverseJoinColumn($joinColumn);
966
        }
967
968
        return $joinTable;
969
    }
970
971
    /**
972
     * Parse the given JoinColumn as JoinColumnMetadata
973
     *
974
     * @return Mapping\JoinColumnMetadata
975
     */
976
    private function convertJoinColumnAnnotationToJoinColumnMetadata(
977
        \ReflectionProperty $reflectionProperty,
978
        Annotation\JoinColumn $joinColumnAnnot,
979
        Mapping\ClassMetadata $classMetadata
980
    ) {
981
        $fieldName            = $reflectionProperty->getName();
982
        $joinColumn           = new Mapping\JoinColumnMetadata();
983
        $columnName           = empty($joinColumnAnnot->name)
984
            ? $this->namingStrategy->propertyToColumnName($fieldName, $classMetadata->getClassName())
985
            : $joinColumnAnnot->name
986
        ;
987
        $referencedColumnName = empty($joinColumnAnnot->referencedColumnName)
988
            ? $this->namingStrategy->referenceColumnName()
989
            : $joinColumnAnnot->referencedColumnName
990
        ;
991
992
        $joinColumn->setColumnName($columnName);
993
        $joinColumn->setReferencedColumnName($referencedColumnName);
994
        $joinColumn->setNullable($joinColumnAnnot->nullable);
995
        $joinColumn->setUnique($joinColumnAnnot->unique);
996
997
        if (! empty($joinColumnAnnot->fieldName)) {
998
            $joinColumn->setAliasedName($joinColumnAnnot->fieldName);
999
        }
1000
1001
        if (! empty($joinColumnAnnot->columnDefinition)) {
1002
            $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition);
1003
        }
1004
1005
        if ($joinColumnAnnot->onDelete) {
1006
            $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete));
1007
        }
1008
1009
        return $joinColumn;
1010
    }
1011
1012
    /**
1013
     * Parses the given method.
1014
     *
1015
     * @return string[]
1016
     */
1017
    private function getMethodCallbacks(\ReflectionMethod $method)
1018
    {
1019
        $annotations = $this->getMethodAnnotations($method);
1020
        $events      = [
1021
            Events::prePersist  => Annotation\PrePersist::class,
1022
            Events::postPersist => Annotation\PostPersist::class,
1023
            Events::preUpdate   => Annotation\PreUpdate::class,
1024
            Events::postUpdate  => Annotation\PostUpdate::class,
1025
            Events::preRemove   => Annotation\PreRemove::class,
1026
            Events::postRemove  => Annotation\PostRemove::class,
1027
            Events::postLoad    => Annotation\PostLoad::class,
1028
            Events::preFlush    => Annotation\PreFlush::class,
1029
        ];
1030
1031
        // Check for callbacks
1032
        $callbacks = [];
1033
1034
        foreach ($events as $eventName => $annotationClassName) {
1035
            if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) {
1036
                $callbacks[] = $eventName;
1037
            }
1038
        }
1039
1040
        return $callbacks;
1041
    }
1042
1043
    /**
1044
     * Attempts to resolve the fetch mode.
1045
     *
1046
     * @param string $className The class name.
1047
     * @param string $fetchMode The fetch mode.
1048
     *
1049
     * @return int The fetch mode as defined in ClassMetadata.
1050
     *
1051
     * @throws Mapping\MappingException If the fetch mode is not valid.
1052
     */
1053
    private function getFetchMode($className, $fetchMode)
1054
    {
1055
        $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode);
1056
1057
        if (! defined($fetchModeConstant)) {
1058
            throw Mapping\MappingException::invalidFetchMode($className, $fetchMode);
1059
        }
1060
1061
        return constant($fetchModeConstant);
1062
    }
1063
1064
    /**
1065
     * @param string   $className        The class name.
1066
     * @param string   $fieldName        The field name.
1067
     * @param string[] $originalCascades The original unprocessed field cascades.
1068
     *
1069
     * @return string[] The processed field cascades.
1070
     *
1071
     * @throws Mapping\MappingException If a cascade option is not valid.
1072
     */
1073
    private function getCascade(string $className, string $fieldName, array $originalCascades)
1074
    {
1075
        $cascadeTypes = ['remove', 'persist', 'refresh'];
1076
        $cascades     = array_map('strtolower', $originalCascades);
1077
1078
        if (in_array('all', $cascades)) {
1079
            $cascades = $cascadeTypes;
1080
        }
1081
1082
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
1083
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
1084
1085
            throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName);
1086
        }
1087
1088
        return $cascades;
1089
    }
1090
1091
    /**
1092
     * @return Annotation\Annotation[]
1093
     */
1094
    private function getClassAnnotations(\ReflectionClass $reflectionClass)
1095
    {
1096
        $classAnnotations = $this->reader->getClassAnnotations($reflectionClass);
1097
1098
        foreach ($classAnnotations as $key => $annot) {
1099
            if (! is_numeric($key)) {
1100
                continue;
1101
            }
1102
1103
            $classAnnotations[get_class($annot)] = $annot;
1104
        }
1105
1106
        return $classAnnotations;
1107
    }
1108
1109
    /**
1110
     *
1111
     * @return Annotation\Annotation[]
1112
     */
1113
    private function getPropertyAnnotations(\ReflectionProperty $reflectionProperty)
1114
    {
1115
        $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty);
1116
1117
        foreach ($propertyAnnotations as $key => $annot) {
1118
            if (! is_numeric($key)) {
1119
                continue;
1120
            }
1121
1122
            $propertyAnnotations[get_class($annot)] = $annot;
1123
        }
1124
1125
        return $propertyAnnotations;
1126
    }
1127
1128
    /**
1129
     *
1130
     * @return Annotation\Annotation[]
1131
     */
1132
    private function getMethodAnnotations(\ReflectionMethod $reflectionMethod)
1133
    {
1134
        $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod);
1135
1136
        foreach ($methodAnnotations as $key => $annot) {
1137
            if (! is_numeric($key)) {
1138
                continue;
1139
            }
1140
1141
            $methodAnnotations[get_class($annot)] = $annot;
1142
        }
1143
1144
        return $methodAnnotations;
1145
    }
1146
}
1147