From 21bc805dc9662ff05f3038b30a29e05591838a29 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 21 Aug 2019 00:32:08 -0400 Subject: [PATCH] Implemented and adopted ClassMetadataBuilder in both Annotation and XML drivers. Implemented and adopted PropertyMetadataBuilder in Annotation driver. --- .../Mapping/Builder/ClassMetadataBuilder.php | 228 +++- .../Mapping/Builder/FieldMetadataBuilder.php | 15 +- .../Builder/PropertyMetadataBuilder.php | 417 +++++++ lib/Doctrine/ORM/Mapping/ClassMetadata.php | 8 + .../ORM/Mapping/Driver/AnnotationDriver.php | 419 +------ .../Mapping/Driver/NewAnnotationDriver.php | 1109 ----------------- lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php | 882 ++++++------- 7 files changed, 1120 insertions(+), 1958 deletions(-) create mode 100644 lib/Doctrine/ORM/Mapping/Builder/PropertyMetadataBuilder.php delete mode 100644 lib/Doctrine/ORM/Mapping/Driver/NewAnnotationDriver.php diff --git a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php index 816a7137253..8df54529193 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/ClassMetadataBuilder.php @@ -7,46 +7,121 @@ use Doctrine\ORM\Annotation; use Doctrine\ORM\Mapping; use function assert; +use function constant; +use function sprintf; class ClassMetadataBuilder { /** @var Mapping\ClassMetadataBuildingContext */ private $metadataBuildingContext; + /** @var TableMetadataBuilder */ + protected $tableMetadataBuilder; + /** @var CacheMetadataBuilder */ protected $cacheMetadataBuilder; + /** @var DiscriminatorColumnMetadataBuilder */ + protected $discriminatorColumnMetadataBuilder; + /** @var string */ private $className; - /** @var Mapping\ComponentMetadata */ + /** @var Mapping\ComponentMetadata|null */ private $parentMetadata; + /** @var Annotation\Entity|null */ + protected $entityAnnotation; + + /** @var Annotation\Embeddable|null */ + protected $embeddableAnnotation; + + /** @var Annotation\MappedSuperclass|null */ + protected $mappedSuperclassAnnotation; + + /** @var Annotation\InheritanceType|null */ + protected $inheritanceTypeAnnotation; + + /** @var Annotation\Table|null */ + protected $tableAnnotation; + /** @var Annotation\Cache|null */ protected $cacheAnnotation; + /** @var Annotation\ChangeTrackingPolicy|null */ + protected $changeTrackingPolicyAnnotation; + + /** @var Annotation\DiscriminatorColumn|null */ + protected $discriminatorColumnAnnotation; + + /** @var Annotation\DiscriminatorMap|null */ + protected $discriminatorMapAnnotation; + public function __construct( Mapping\ClassMetadataBuildingContext $metadataBuildingContext, - ?CacheMetadataBuilder $cacheMetadataBuilder = null + ?TableMetadataBuilder $tableMetadataBuilder = null, + ?CacheMetadataBuilder $cacheMetadataBuilder = null, + ?DiscriminatorColumnMetadataBuilder $discriminatorColumnMetadataBuilder = null ) { - $this->metadataBuildingContext = $metadataBuildingContext; - $this->cacheMetadataBuilder = $cacheMetadataBuilder ?: new CacheMetadataBuilder($metadataBuildingContext); + $this->metadataBuildingContext = $metadataBuildingContext; + $this->tableMetadataBuilder = $tableMetadataBuilder ?: new TableMetadataBuilder($metadataBuildingContext); + $this->cacheMetadataBuilder = $cacheMetadataBuilder ?: new CacheMetadataBuilder($metadataBuildingContext); + $this->discriminatorColumnMetadataBuilder = $discriminatorColumnMetadataBuilder?: new DiscriminatorColumnMetadataBuilder($metadataBuildingContext); } - public function getClassName(string $className) : ClassMetadataBuilder + public function withClassName(string $className) : ClassMetadataBuilder { $this->className = $className; return $this; } - public function withParentMetadata(Mapping\ComponentMetadata $parentMetadata) : ClassMetadataBuilder + public function withParentMetadata(?Mapping\ComponentMetadata $parentMetadata) : ClassMetadataBuilder { $this->parentMetadata = $parentMetadata; return $this; } + public function withEntityAnnotation(?Annotation\Entity $entityAnnotation) : ClassMetadataBuilder + { + $this->entityAnnotation = $entityAnnotation; + + return $this; + } + + public function withEmbeddableAnnotation(?Annotation\Embeddable $embeddableAnnotation) : ClassMetadataBuilder + { + $this->embeddableAnnotation = $embeddableAnnotation; + + return $this; + } + + public function withMappedSuperclassAnnotation(?Annotation\MappedSuperclass $mappedSuperclassAnnotation) : ClassMetadataBuilder + { + $this->mappedSuperclassAnnotation = $mappedSuperclassAnnotation; + + return $this; + } + + public function withInheritanceTypeAnnotation(?Annotation\InheritanceType $inheritanceTypeAnnotation) : ClassMetadataBuilder + { + $this->inheritanceTypeAnnotation = $inheritanceTypeAnnotation; + + return $this; + } + + public function withTableAnnotation(?Annotation\Table $tableAnnotation) : ClassMetadataBuilder + { + $this->tableAnnotation = $tableAnnotation; + + if ($tableAnnotation !== null) { + $this->tableMetadataBuilder->withTableAnnotation($tableAnnotation); + } + + return $this; + } + public function withCacheAnnotation(?Annotation\Cache $cacheAnnotation) : ClassMetadataBuilder { $this->cacheAnnotation = $cacheAnnotation; @@ -58,6 +133,31 @@ public function withCacheAnnotation(?Annotation\Cache $cacheAnnotation) : ClassM return $this; } + public function withChangeTrackingPolicyAnnotation(?Annotation\ChangeTrackingPolicy $changeTrackingPolicyAnnotation) : ClassMetadataBuilder + { + $this->changeTrackingPolicyAnnotation = $changeTrackingPolicyAnnotation; + + return $this; + } + + public function withDiscriminatorColumnAnnotation(?Annotation\DiscriminatorColumn $discriminatorColumnAnnotation) : ClassMetadataBuilder + { + $this->discriminatorColumnAnnotation = $discriminatorColumnAnnotation; + + if ($discriminatorColumnAnnotation !== null) { + $this->discriminatorColumnMetadataBuilder->withDiscriminatorColumnAnnotation($discriminatorColumnAnnotation); + } + + return $this; + } + + public function withDiscriminatorMapAnnotation(?Annotation\DiscriminatorMap $discriminatorMapAnnotation) : ClassMetadataBuilder + { + $this->discriminatorMapAnnotation = $discriminatorMapAnnotation; + + return $this; + } + public function build() : Mapping\ClassMetadata { assert($this->className !== null); @@ -68,15 +168,129 @@ public function build() : Mapping\ClassMetadata $classMetadata = new Mapping\ClassMetadata($className, $this->parentMetadata); + switch (true) { + case $this->entityAnnotation !== null: + $this->buildEntityClassMetadata($classMetadata); + break; + + case $this->mappedSuperclassAnnotation !== null: + $this->buildMappedSuperclassMetadata($classMetadata); + break; + + case $this->embeddableAnnotation !== null: + $this->buildEmbeddableMetadata($classMetadata); + break; + + default: + throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($this->className); + } + $this->buildCache($classMetadata); return $classMetadata; } + protected function buildEntityClassMetadata(Mapping\ClassMetadata $classMetadata) : void + { + $classMetadata->isMappedSuperclass = false; + $classMetadata->isEmbeddedClass = false; + + if ($this->entityAnnotation->repositoryClass !== null) { + $classMetadata->setCustomRepositoryClassName($this->entityAnnotation->repositoryClass); + } + + if ($this->entityAnnotation->readOnly) { + $classMetadata->asReadOnly(); + } + + $this->buildTable($classMetadata); + $this->buildInheritance($classMetadata); + $this->buildChangeTrackingPolicy($classMetadata); + } + + protected function buildMappedSuperclassMetadata(Mapping\ClassMetadata $classMetadata) : void + { + $classMetadata->isMappedSuperclass = true; + $classMetadata->isEmbeddedClass = false; + + if ($this->mappedSuperclassAnnotation->repositoryClass !== null) { + $classMetadata->setCustomRepositoryClassName($this->mappedSuperclassAnnotation->repositoryClass); + } + } + + protected function buildEmbeddableMetadata(Mapping\ClassMetadata $classMetadata) : void + { + $classMetadata->isMappedSuperclass = false; + $classMetadata->isEmbeddedClass = true; + } + + protected function buildTable(Mapping\ClassMetadata $classMetadata) : void + { + $parentMetadata = $classMetadata->getParent(); + $tableMetadata = null; + + if ($parentMetadata instanceof Mapping\ClassMetadata + && $parentMetadata->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) { + // Handle the case where a middle mapped super class inherits from a single table inheritance tree. + do { + if (! $parentMetadata->isMappedSuperclass) { + $tableMetadata = $parentMetadata->table; + break; + } + + $parentMetadata = $parentMetadata->getParent(); + } while ($parentMetadata !== null); + } else { + $tableMetadata = $this->tableMetadataBuilder + ->withEntityClassMetadata($classMetadata) + ->withTableAnnotation($this->tableAnnotation) + ->build(); + } + + $classMetadata->setTable($tableMetadata); + } + + protected function buildInheritance(Mapping\ClassMetadata $classMetadata) : void + { + if ($this->inheritanceTypeAnnotation !== null) { + $typeName = $this->inheritanceTypeAnnotation->value; + $type = constant(sprintf('%s::%s', Mapping\InheritanceType::class, $typeName)); + + $classMetadata->setInheritanceType($type); + + if ($type !== Mapping\InheritanceType::NONE) { + $discriminatorColumn = $this->discriminatorColumnMetadataBuilder + ->withComponentMetadata($classMetadata) + ->withDiscriminatorColumnAnnotation($this->discriminatorColumnAnnotation) + ->build(); + + $classMetadata->setDiscriminatorColumn($discriminatorColumn); + + if ($this->discriminatorMapAnnotation !== null) { + $classMetadata->setDiscriminatorMap($this->discriminatorMapAnnotation->value); + } + } + } + } + protected function buildCache(Mapping\ClassMetadata $classMetadata) : void { if ($this->cacheAnnotation !== null) { - $classMetadata->setCache($this->cacheMetadataBuilder->build()); + $cacheMetadata = $this->cacheMetadataBuilder + ->withComponentMetadata($classMetadata) + ->build(); + + $classMetadata->setCache($cacheMetadata); + } + } + + protected function buildChangeTrackingPolicy(Mapping\ClassMetadata $classMetadata) : void + { + if ($this->changeTrackingPolicyAnnotation !== null) { + $policyName = $this->changeTrackingPolicyAnnotation->value; + $policy = constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $policyName)); + + $classMetadata->setChangeTrackingPolicy($policy); } } } diff --git a/lib/Doctrine/ORM/Mapping/Builder/FieldMetadataBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/FieldMetadataBuilder.php index 856a2c0763c..5d7ccde4db0 100644 --- a/lib/Doctrine/ORM/Mapping/Builder/FieldMetadataBuilder.php +++ b/lib/Doctrine/ORM/Mapping/Builder/FieldMetadataBuilder.php @@ -85,25 +85,22 @@ public function withVersionAnnotation(?Annotation\Version $versionAnnotation) : return $this; } - public function withGeneratedValueAnnotation( - ?Annotation\GeneratedValue $generatedValueAnnotation - ) : FieldMetadataBuilder { + public function withGeneratedValueAnnotation(?Annotation\GeneratedValue $generatedValueAnnotation) : FieldMetadataBuilder + { $this->generatedValueAnnotation = $generatedValueAnnotation; return $this; } - public function withSequenceGeneratorAnnotation( - ?Annotation\SequenceGenerator $sequenceGeneratorAnnotation - ) : FieldMetadataBuilder { + public function withSequenceGeneratorAnnotation(?Annotation\SequenceGenerator $sequenceGeneratorAnnotation) : FieldMetadataBuilder + { $this->sequenceGeneratorAnnotation = $sequenceGeneratorAnnotation; return $this; } - public function withCustomIdGeneratorAnnotation( - ?Annotation\CustomIdGenerator $customIdGeneratorAnnotation - ) : FieldMetadataBuilder { + public function withCustomIdGeneratorAnnotation(?Annotation\CustomIdGenerator $customIdGeneratorAnnotation) : FieldMetadataBuilder + { $this->customIdGeneratorAnnotation = $customIdGeneratorAnnotation; return $this; diff --git a/lib/Doctrine/ORM/Mapping/Builder/PropertyMetadataBuilder.php b/lib/Doctrine/ORM/Mapping/Builder/PropertyMetadataBuilder.php new file mode 100644 index 00000000000..684deb3e121 --- /dev/null +++ b/lib/Doctrine/ORM/Mapping/Builder/PropertyMetadataBuilder.php @@ -0,0 +1,417 @@ +metadataBuildingContext = $metadataBuildingContext; + $this->fieldMetadataBuilder = $fieldMetadataBuilder ?: new FieldMetadataBuilder($metadataBuildingContext); + $this->oneToOneAssociationMetadataBuilder = $oneToOneAssociationMetadataBuilder ?: new OneToOneAssociationMetadataBuilder($metadataBuildingContext); + $this->manyToOneAssociationMetadataBuilder = $manyToOneAssociationMetadataBuilder ?: new ManyToOneAssociationMetadataBuilder($metadataBuildingContext); + $this->oneToManyAssociationMetadataBuilder = $oneToManyAssociationMetadataBuilder ?: new OneToManyAssociationMetadataBuilder($metadataBuildingContext); + $this->manyToManyAssociationMetadataBuilder = $manyToManyAssociationMetadataBuilder ?: new ManyToManyAssociationMetadataBuilder($metadataBuildingContext); + $this->transientMetadataBuilder = $transientMetadataBuilder ?: new TransientMetadataBuilder($metadataBuildingContext); + } + + public function withComponentMetadata(Mapping\ClassMetadata $componentMetadata) : PropertyMetadataBuilder + { + $this->componentMetadata = $componentMetadata; + + $this->fieldMetadataBuilder->withComponentMetadata($componentMetadata); + $this->oneToOneAssociationMetadataBuilder->withComponentMetadata($componentMetadata); + $this->manyToOneAssociationMetadataBuilder->withComponentMetadata($componentMetadata); + $this->oneToManyAssociationMetadataBuilder->withComponentMetadata($componentMetadata); + $this->manyToManyAssociationMetadataBuilder->withComponentMetadata($componentMetadata); + $this->transientMetadataBuilder->withComponentMetadata($componentMetadata); + + return $this; + } + + public function withFieldName(string $fieldName) : PropertyMetadataBuilder + { + $this->fieldName = $fieldName; + + $this->fieldMetadataBuilder->withFieldName($fieldName); + $this->oneToOneAssociationMetadataBuilder->withFieldName($fieldName); + $this->manyToOneAssociationMetadataBuilder->withFieldName($fieldName); + $this->oneToManyAssociationMetadataBuilder->withFieldName($fieldName); + $this->manyToManyAssociationMetadataBuilder->withFieldName($fieldName); + $this->transientMetadataBuilder->withFieldName($fieldName); + + return $this; + } + + public function withIdAnnotation(?Annotation\Id $idAnnotation) : PropertyMetadataBuilder + { + $this->idAnnotation = $idAnnotation; + + $this->fieldMetadataBuilder->withIdAnnotation($idAnnotation); + $this->oneToOneAssociationMetadataBuilder->withIdAnnotation($idAnnotation); + $this->manyToOneAssociationMetadataBuilder->withIdAnnotation($idAnnotation); + + return $this; + } + + public function withCacheAnnotation(?Annotation\Cache $cacheAnnotation) : PropertyMetadataBuilder + { + $this->cacheAnnotation = $cacheAnnotation; + + $this->oneToOneAssociationMetadataBuilder->withCacheAnnotation($cacheAnnotation); + $this->manyToOneAssociationMetadataBuilder->withCacheAnnotation($cacheAnnotation); + $this->oneToManyAssociationMetadataBuilder->withCacheAnnotation($cacheAnnotation); + $this->manyToManyAssociationMetadataBuilder->withCacheAnnotation($cacheAnnotation); + + return $this; + } + + public function withColumnAnnotation(?Annotation\Column $columnAnnotation) : PropertyMetadataBuilder + { + $this->columnAnnotation = $columnAnnotation; + + if ($columnAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->embeddedAnnotation = null; + $this->oneToOneAnnotation = null; + $this->manyToOneAnnotation = null; + $this->oneToManyAnnotation = null; + $this->manyToManyAnnotation = null; + + $this->fieldMetadataBuilder->withColumnAnnotation($columnAnnotation); + } + + return $this; + } + + public function withEmbeddedAnnotation(?Annotation\Embedded $embeddedAnnotation) : PropertyMetadataBuilder + { + $this->embeddedAnnotation = $embeddedAnnotation; + + if ($embeddedAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->columnAnnotation = null; + $this->oneToOneAnnotation = null; + $this->manyToOneAnnotation = null; + $this->oneToManyAnnotation = null; + $this->manyToManyAnnotation = null; + + // $this->embeddedMetadataBuilder->withEmbeddedAnnotation($embeddedAnnotation); + } + + return $this; + } + + public function withOneToOneAnnotation(?Annotation\OneToOne $oneToOneAnnotation) : PropertyMetadataBuilder + { + $this->oneToOneAnnotation = $oneToOneAnnotation; + + if ($oneToOneAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->columnAnnotation = null; + $this->embeddedAnnotation = null; + $this->manyToOneAnnotation = null; + $this->oneToManyAnnotation = null; + $this->manyToManyAnnotation = null; + + $this->oneToOneAssociationMetadataBuilder->withOneToOneAnnotation($oneToOneAnnotation); + } + + return $this; + } + + public function withManyToOneAnnotation(?Annotation\ManyToOne $manyToOneAnnotation) : PropertyMetadataBuilder + { + $this->manyToOneAnnotation = $manyToOneAnnotation; + + if ($manyToOneAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->columnAnnotation = null; + $this->embeddedAnnotation = null; + $this->oneToOneAnnotation = null; + $this->oneToManyAnnotation = null; + $this->manyToManyAnnotation = null; + + $this->manyToOneAssociationMetadataBuilder->withManyToOneAnnotation($manyToOneAnnotation); + } + + return $this; + } + + public function withOneToManyAnnotation(?Annotation\OneToMany $oneToManyAnnotation) : PropertyMetadataBuilder + { + $this->oneToManyAnnotation = $oneToManyAnnotation; + + if ($oneToManyAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->columnAnnotation = null; + $this->embeddedAnnotation = null; + $this->oneToOneAnnotation = null; + $this->manyToOneAnnotation = null; + $this->manyToManyAnnotation = null; + + $this->oneToManyAssociationMetadataBuilder->withOneToManyAnnotation($oneToManyAnnotation); + } + + return $this; + } + + public function withManyToManyAnnotation(?Annotation\ManyToMany $manyToManyAnnotation) : PropertyMetadataBuilder + { + $this->manyToManyAnnotation = $manyToManyAnnotation; + + if ($manyToManyAnnotation !== null) { + // Make sure all other property type annotations are cleared + $this->columnAnnotation = null; + $this->embeddedAnnotation = null; + $this->oneToOneAnnotation = null; + $this->manyToOneAnnotation = null; + $this->oneToManyAnnotation = null; + + $this->manyToManyAssociationMetadataBuilder->withManyToManyAnnotation($manyToManyAnnotation); + } + + return $this; + } + + public function withJoinTableAnnotation(?Annotation\JoinTable $joinTableAnnotation) : PropertyMetadataBuilder + { + $this->joinTableAnnotation = $joinTableAnnotation; + + $this->manyToManyAssociationMetadataBuilder->withJoinTableAnnotation($joinTableAnnotation); + + return $this; + } + + public function withJoinColumnsAnnotation(?Annotation\JoinColumns $joinColumnsAnnotation) : PropertyMetadataBuilder + { + $this->joinColumnsAnnotation = $joinColumnsAnnotation; + + $this->oneToOneAssociationMetadataBuilder->withJoinColumnsAnnotation($joinColumnsAnnotation); + $this->manyToOneAssociationMetadataBuilder->withJoinColumnsAnnotation($joinColumnsAnnotation); + + return $this; + } + + public function withJoinColumnAnnotation(?Annotation\JoinColumn $joinColumnAnnotation) : PropertyMetadataBuilder + { + $this->joinColumnAnnotation = $joinColumnAnnotation; + + $this->oneToOneAssociationMetadataBuilder->withJoinColumnAnnotation($joinColumnAnnotation); + $this->manyToOneAssociationMetadataBuilder->withJoinColumnAnnotation($joinColumnAnnotation); + + return $this; + } + + public function withOrderByAnnotation(?Annotation\OrderBy $orderByAnnotation) : PropertyMetadataBuilder + { + $this->orderByAnnotation = $orderByAnnotation; + + $this->oneToManyAssociationMetadataBuilder->withOrderByAnnotation($orderByAnnotation); + $this->manyToManyAssociationMetadataBuilder->withOrderByAnnotation($orderByAnnotation); + + return $this; + } + + public function withVersionAnnotation(?Annotation\Version $versionAnnotation) : PropertyMetadataBuilder + { + $this->versionAnnotation = $versionAnnotation; + + $this->fieldMetadataBuilder->withVersionAnnotation($versionAnnotation); + + return $this; + } + + public function withGeneratedValueAnnotation(?Annotation\GeneratedValue $generatedValueAnnotation) : PropertyMetadataBuilder + { + $this->generatedValueAnnotation = $generatedValueAnnotation; + + $this->fieldMetadataBuilder->withGeneratedValueAnnotation($this->generatedValueAnnotation); + + return $this; + } + + public function withSequenceGeneratorAnnotation(?Annotation\SequenceGenerator $sequenceGeneratorAnnotation) : PropertyMetadataBuilder + { + $this->sequenceGeneratorAnnotation = $sequenceGeneratorAnnotation; + + $this->fieldMetadataBuilder->withSequenceGeneratorAnnotation($this->sequenceGeneratorAnnotation); + + return $this; + } + + public function withCustomIdGeneratorAnnotation(?Annotation\CustomIdGenerator $customIdGeneratorAnnotation) : PropertyMetadataBuilder + { + $this->customIdGeneratorAnnotation = $customIdGeneratorAnnotation; + + $this->fieldMetadataBuilder->withCustomIdGeneratorAnnotation($this->customIdGeneratorAnnotation); + + return $this; + } + + public function build() : ?Mapping\Property + { + // Validate required fields + assert($this->componentMetadata !== null); + assert($this->fieldName !== null); + + $componentClassName = $this->componentMetadata->getClassName(); + + switch (true) { + case $this->columnAnnotation !== null: + $propertyMetadata = $this->fieldMetadataBuilder->build(); + + // Prevent column duplication + $columnName = $propertyMetadata->getColumnName(); + + if ($this->componentMetadata->checkPropertyDuplication($columnName)) { + throw Mapping\MappingException::duplicateColumnName($componentClassName, $columnName); + } + + return $propertyMetadata; + case $this->embeddedAnnotation !== null: + // @todo guilhermeblanco Remove nullable typehint once embeddeds are back + return null; + case $this->oneToOneAnnotation !== null: + return $this->oneToOneAssociationMetadataBuilder->build(); + + // Prevent column duplication + // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. +// foreach ($propertyMetadata->getJoinColumns() as $joinColumnMetadata) { +// $columnName = $joinColumnMetadata->getColumnName(); +// +// if ($this->componentMetadata->checkPropertyDuplication($columnName)) { +// throw Mapping\MappingException::duplicateColumnName($componentClassName, $columnName); +// } +// } +// +// return $propertyMetadata; + case $this->manyToOneAnnotation !== null: + return $this->manyToOneAssociationMetadataBuilder->build(); + + // Prevent column duplication + // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. +// foreach ($propertyMetadata->getJoinColumns() as $joinColumnMetadata) { +// $columnName = $joinColumnMetadata->getColumnName(); +// +// if ($this->componentMetadata->checkPropertyDuplication($columnName)) { +// throw Mapping\MappingException::duplicateColumnName($componentClassName, $columnName); +// } +// } +// +// return $propertyMetadata; + case $this->oneToManyAnnotation !== null: + $propertyMetadata = $this->oneToManyAssociationMetadataBuilder->build(); + + if ($this->componentMetadata->isMappedSuperclass && ! $propertyMetadata->isOwningSide()) { + throw Mapping\MappingException::illegalToManyAssociationOnMappedSuperclass( + $componentClassName, + $this->fieldName + ); + } + + return $propertyMetadata; + case $this->manyToManyAnnotation !== null: + $propertyMetadata = $this->manyToManyAssociationMetadataBuilder->build(); + + if ($this->componentMetadata->isMappedSuperclass && ! $propertyMetadata->isOwningSide()) { + throw Mapping\MappingException::illegalToManyAssociationOnMappedSuperclass( + $componentClassName, + $this->fieldName + ); + } + + return $propertyMetadata; + default: + return $this->transientMetadataBuilder->build(); + } + } +} diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 2c26df9cb5f..557627e16d6 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -796,6 +796,14 @@ public function addProperty(Property $property) : void $this->versionProperty = $property; } + $this->fieldNames[$property->getColumnName()] = $property->getName(); + break; + + case $property instanceof ToOneAssociationMetadata: + foreach ($property->getJoinColumns() as $joinColumnMetadata) { + $this->fieldNames[$joinColumnMetadata->getColumnName()] = $property->getName(); + } + break; default: diff --git a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php index 79e1155b25a..4e8d2a01394 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/AnnotationDriver.php @@ -34,7 +34,6 @@ use function preg_match; use function preg_quote; use function realpath; -use function sprintf; use function str_replace; use function strpos; @@ -279,359 +278,33 @@ public function loadMetadataForClass( Mapping\ClassMetadataBuildingContext $metadataBuildingContext ) : Mapping\ComponentMetadata { $reflectionClass = new ReflectionClass($className); - $metadata = new Mapping\ClassMetadata($className, $parent); $classAnnotations = $this->getClassAnnotations($reflectionClass); - $classMetadata = $this->convertClassAnnotationsToClassMetadata( - $classAnnotations, - $reflectionClass, - $metadata, - $metadataBuildingContext - ); - - // Evaluate @Cache annotation - if (isset($classAnnotations[Annotation\Cache::class])) { - $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext); - - $cacheBuilder - ->withComponentMetadata($metadata) - ->withCacheAnnotation($classAnnotations[Annotation\Cache::class]); - - $metadata->setCache($cacheBuilder->build()); - } - - // Evaluate annotations on properties/fields - /** @var ReflectionProperty $reflProperty */ - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) { - continue; - } - - $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); - $property = $this->convertPropertyAnnotationsToProperty( - $propertyAnnotations, - $reflectionProperty, - $classMetadata, - $metadataBuildingContext - ); - - if ($classMetadata->isMappedSuperclass && - $property instanceof Mapping\ToManyAssociationMetadata && - ! $property->isOwningSide()) { - throw Mapping\MappingException::illegalToManyAssociationOnMappedSuperclass( - $classMetadata->getClassName(), - $property->getName() - ); - } - - $metadata->addProperty($property); + $classBuilder = new Builder\ClassMetadataBuilder($metadataBuildingContext); + $classMetadata = $classBuilder + ->withClassName($reflectionClass->getName()) + ->withParentMetadata($parent) + ->withEntityAnnotation($classAnnotations[Annotation\Entity::class] ?? null) + ->withMappedSuperclassAnnotation($classAnnotations[Annotation\MappedSuperclass::class] ?? null) + ->withEmbeddableAnnotation($classAnnotations[Annotation\Embeddable::class] ?? null) + ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null) + ->withInheritanceTypeAnnotation($classAnnotations[Annotation\InheritanceType::class] ?? null) + ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null) + ->withDiscriminatorMapAnnotation($classAnnotations[Annotation\DiscriminatorMap::class] ?? null) + ->withChangeTrackingPolicyAnnotation($classAnnotations[Annotation\ChangeTrackingPolicy::class] ?? null) + ->withCacheAnnotation($classAnnotations[Annotation\Cache::class] ?? null) + ->build(); + + if (! $classMetadata->isEmbeddedClass) { + $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $classMetadata); + $this->attachEntityListeners($classAnnotations, $classMetadata); } - $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $metadata, $metadataBuildingContext); + $this->attachProperties($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext); + $this->attachPropertyOverrides($classAnnotations, $reflectionClass, $classMetadata, $metadataBuildingContext); return $classMetadata; } - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @throws Mapping\MappingException - * @throws UnexpectedValueException - * @throws ReflectionException - */ - private function convertClassAnnotationsToClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - Mapping\ClassMetadata $metadata, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) : Mapping\ClassMetadata { - switch (true) { - case isset($classAnnotations[Annotation\Entity::class]): - return $this->convertClassAnnotationsToEntityClassMetadata( - $classAnnotations, - $reflectionClass, - $metadata, - $metadataBuildingContext - ); - - break; - - case isset($classAnnotations[Annotation\MappedSuperclass::class]): - return $this->convertClassAnnotationsToMappedSuperClassMetadata( - $classAnnotations, - $reflectionClass, - $metadata - ); - case isset($classAnnotations[Annotation\Embeddable::class]): - return $this->convertClassAnnotationsToEmbeddableClassMetadata( - $classAnnotations, - $reflectionClass, - $metadata - ); - default: - throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName()); - } - } - - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @return Mapping\ClassMetadata - * - * @throws Mapping\MappingException - * @throws ReflectionException - * @throws UnexpectedValueException - */ - private function convertClassAnnotationsToEntityClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - Mapping\ClassMetadata $metadata, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) { - /** @var Annotation\Entity $entityAnnot */ - $entityAnnot = $classAnnotations[Annotation\Entity::class]; - - if ($entityAnnot->repositoryClass !== null) { - $metadata->setCustomRepositoryClassName($entityAnnot->repositoryClass); - } - - if ($entityAnnot->readOnly) { - $metadata->asReadOnly(); - } - - $metadata->isMappedSuperclass = false; - $metadata->isEmbeddedClass = false; - - // Process table information - $parent = $metadata->getParent(); - - if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) { - // Handle the case where a middle mapped super class inherits from a single table inheritance tree. - do { - if (! $parent->isMappedSuperclass) { - $metadata->setTable($parent->table); - - break; - } - - $parent = $parent->getParent(); - } while ($parent !== null); - } else { - $tableBuilder = new Builder\TableMetadataBuilder($metadataBuildingContext); - - $tableBuilder - ->withEntityClassMetadata($metadata) - ->withTableAnnotation($classAnnotations[Annotation\Table::class] ?? null); - - $metadata->setTable($tableBuilder->build()); - } - - // Evaluate @ChangeTrackingPolicy annotation - if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) { - $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class]; - - $metadata->setChangeTrackingPolicy( - constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value)) - ); - } - - // Evaluate @InheritanceType annotation - if (isset($classAnnotations[Annotation\InheritanceType::class])) { - $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class]; - - $metadata->setInheritanceType( - constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value)) - ); - - if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) { - $discriminatorColumnBuilder = new Builder\DiscriminatorColumnMetadataBuilder($metadataBuildingContext); - - $discriminatorColumnBuilder - ->withComponentMetadata($metadata) - ->withDiscriminatorColumnAnnotation($classAnnotations[Annotation\DiscriminatorColumn::class] ?? null); - - $metadata->setDiscriminatorColumn($discriminatorColumnBuilder->build()); - - // Evaluate DiscriminatorMap annotation - if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) { - $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class]; - $discriminatorMap = $discriminatorMapAnnotation->value; - - $metadata->setDiscriminatorMap($discriminatorMap); - } - } - } - - $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); - $this->attachEntityListeners($classAnnotations, $metadata); - - return $metadata; - } - - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @throws Mapping\MappingException - * @throws ReflectionException - */ - private function convertClassAnnotationsToMappedSuperClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - Mapping\ClassMetadata $metadata - ) : Mapping\ClassMetadata { - /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */ - $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class]; - - if ($mappedSuperclassAnnot->repositoryClass !== null) { - $metadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass); - } - - $metadata->isMappedSuperclass = true; - $metadata->isEmbeddedClass = false; - - $this->attachLifecycleCallbacks($classAnnotations, $reflectionClass, $metadata); - $this->attachEntityListeners($classAnnotations, $metadata); - - return $metadata; - } - - /** - * @param Annotation\Annotation[] $classAnnotations - */ - private function convertClassAnnotationsToEmbeddableClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - Mapping\ClassMetadata $metadata - ) : Mapping\ClassMetadata { - $metadata->isMappedSuperclass = false; - $metadata->isEmbeddedClass = true; - - return $metadata; - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @todo guilhermeblanco Remove nullable typehint once embeddables are back - */ - private function convertPropertyAnnotationsToProperty( - array $propertyAnnotations, - ReflectionProperty $reflectionProperty, - Mapping\ClassMetadata $metadata, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) : ?Mapping\Property { - switch (true) { - case isset($propertyAnnotations[Annotation\Column::class]): - $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext); - $fieldMetadata = $fieldBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class]) - ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) - ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null) - ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null) - ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null) - ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null) - ->build(); - - // Prevent column duplication - $columnName = $fieldMetadata->getColumnName(); - - if ($metadata->checkPropertyDuplication($columnName)) { - throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); - } - - $metadata->fieldNames[$fieldMetadata->getColumnName()] = $fieldMetadata->getName(); - - return $fieldMetadata; - case isset($propertyAnnotations[Annotation\OneToOne::class]): - $oneToOneAssociationBuilder = new Builder\OneToOneAssociationMetadataBuilder($metadataBuildingContext); - $associationMetadata = $oneToOneAssociationBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->withOneToOneAnnotation($propertyAnnotations[Annotation\OneToOne::class] ?? null) - ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) - ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) - ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null) - ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null) - ->build(); - - // Prevent column duplication - foreach ($associationMetadata->getJoinColumns() as $joinColumnMetadata) { - $columnName = $joinColumnMetadata->getColumnName(); - - // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. - //if ($metadata->checkPropertyDuplication($columnName)) { - // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); - //} - - if ($associationMetadata->isOwningSide()) { - $metadata->fieldNames[$columnName] = $associationMetadata->getName(); - } - } - - return $associationMetadata; - case isset($propertyAnnotations[Annotation\ManyToOne::class]): - $manyToOneAssociationBuilder = new Builder\ManyToOneAssociationMetadataBuilder($metadataBuildingContext); - $associationMetadata = $manyToOneAssociationBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->withManyToOneAnnotation($propertyAnnotations[Annotation\ManyToOne::class] ?? null) - ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) - ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) - ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null) - ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null) - ->build(); - - // Prevent column duplication - foreach ($associationMetadata->getJoinColumns() as $joinColumnMetadata) { - $columnName = $joinColumnMetadata->getColumnName(); - - // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. - //if ($metadata->checkPropertyDuplication($columnName)) { - // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); - //} - - if ($associationMetadata->isOwningSide()) { - $metadata->fieldNames[$columnName] = $associationMetadata->getName(); - } - } - - return $associationMetadata; - case isset($propertyAnnotations[Annotation\OneToMany::class]): - $oneToManyAssociationBuilder = new Builder\OneToManyAssociationMetadataBuilder($metadataBuildingContext); - - return $oneToManyAssociationBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->withOneToManyAnnotation($propertyAnnotations[Annotation\OneToMany::class] ?? null) - ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) - ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) - ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null) - ->build(); - case isset($propertyAnnotations[Annotation\ManyToMany::class]): - $manyToManyAssociationBuilder = new Builder\ManyToManyAssociationMetadataBuilder($metadataBuildingContext); - - return $manyToManyAssociationBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->withManyToManyAnnotation($propertyAnnotations[Annotation\ManyToMany::class] ?? null) - ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) - ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) - ->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class] ?? null) - ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null) - ->build(); - case isset($propertyAnnotations[Annotation\Embedded::class]): - return null; - default: - $transientBuilder = new Builder\TransientMetadataBuilder($metadataBuildingContext); - - return $transientBuilder - ->withComponentMetadata($metadata) - ->withFieldName($reflectionProperty->getName()) - ->build(); - } - } - /** * @param Annotation\Annotation[] $classAnnotations */ @@ -715,6 +388,52 @@ private function attachEntityListeners( } } + /** + * @param Annotation\Annotation[] $classAnnotations + * + * @throws Mapping\MappingException + */ + private function attachProperties( + array $classAnnotations, + ReflectionClass $reflectionClass, + Mapping\ClassMetadata $metadata, + Mapping\ClassMetadataBuildingContext $metadataBuildingContext + ) : void { + // Evaluate annotations on properties/fields + $propertyBuilder = new Builder\PropertyMetadataBuilder($metadataBuildingContext); + + /** @var ReflectionProperty $reflProperty */ + foreach ($reflectionClass->getProperties() as $reflectionProperty) { + if ($reflectionProperty->getDeclaringClass()->getName() !== $reflectionClass->getName()) { + continue; + } + + $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); + $propertyMetadata = $propertyBuilder + ->withComponentMetadata($metadata) + ->withFieldName($reflectionProperty->getName()) + ->withIdAnnotation($propertyAnnotations[Annotation\Id::class] ?? null) + ->withCacheAnnotation($propertyAnnotations[Annotation\Cache::class] ?? null) + ->withColumnAnnotation($propertyAnnotations[Annotation\Column::class] ?? null) + ->withEmbeddedAnnotation($propertyAnnotations[Annotation\Embedded::class] ?? null) + ->withOneToOneAnnotation($propertyAnnotations[Annotation\OneToOne::class] ?? null) + ->withManyToOneAnnotation($propertyAnnotations[Annotation\ManyToOne::class] ?? null) + ->withOneToManyAnnotation($propertyAnnotations[Annotation\OneToMany::class] ?? null) + ->withManyToManyAnnotation($propertyAnnotations[Annotation\ManyToMany::class] ?? null) + ->withJoinTableAnnotation($propertyAnnotations[Annotation\JoinTable::class] ?? null) + ->withJoinColumnsAnnotation($propertyAnnotations[Annotation\JoinColumns::class] ?? null) + ->withJoinColumnAnnotation($propertyAnnotations[Annotation\JoinColumn::class] ?? null) + ->withOrderByAnnotation($propertyAnnotations[Annotation\OrderBy::class] ?? null) + ->withVersionAnnotation($propertyAnnotations[Annotation\Version::class] ?? null) + ->withGeneratedValueAnnotation($propertyAnnotations[Annotation\GeneratedValue::class] ?? null) + ->withSequenceGeneratorAnnotation($propertyAnnotations[Annotation\SequenceGenerator::class] ?? null) + ->withCustomIdGeneratorAnnotation($propertyAnnotations[Annotation\CustomIdGenerator::class] ?? null) + ->build(); + + $metadata->addProperty($propertyMetadata); + } + } + /** * @param Annotation\Annotation[] $classAnnotations * @@ -761,10 +480,6 @@ private function attachPropertyOverrides( // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); //} - if ($override->isOwningSide()) { - $metadata->fieldNames[$columnName] = $fieldName; - } - $joinColumns[] = $joinColumnMetadata; } @@ -828,8 +543,6 @@ private function attachPropertyOverrides( throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); } - $metadata->fieldNames[$fieldMetadata->getColumnName()] = $fieldName; - $metadata->setPropertyOverride($fieldMetadata); } } diff --git a/lib/Doctrine/ORM/Mapping/Driver/NewAnnotationDriver.php b/lib/Doctrine/ORM/Mapping/Driver/NewAnnotationDriver.php deleted file mode 100644 index b698e307058..00000000000 --- a/lib/Doctrine/ORM/Mapping/Driver/NewAnnotationDriver.php +++ /dev/null @@ -1,1109 +0,0 @@ - 1, - Annotation\MappedSuperclass::class => 2, - ]; - - /** - * The Annotation reader. - * - * @var AnnotationReader - */ - protected $reader; - - /** - * The file locator. - * - * @var FileLocator - */ - protected $locator; - - /** @var Factory\NamingStrategy */ - protected $namingStrategy; - - /** - * Cache for AnnotationDriver#getAllClassNames(). - * - * @var string[]|null - */ - private $classNames; - - /** - * Initializes a new AnnotationDriver that uses the given AnnotationReader for reading docblock annotations. - * - * @param AnnotationReader $reader The AnnotationReader to use, duck-typed. - * @param FileLocator $locator A FileLocator or one/multiple paths where mapping documents can be found. - * @param Factory\NamingStrategy $namingStrategy The NamingStrategy to use. - */ - public function __construct(AnnotationReader $reader, FileLocator $locator, Factory\NamingStrategy $namingStrategy) - { - $this->reader = $reader; - $this->locator = $locator; - $this->namingStrategy = $namingStrategy; - } - - /** - * {@inheritdoc} - * - * @return Mapping\ComponentMetadata - * - * @throws Mapping\MappingException - * @throws ReflectionException - */ - public function loadMetadataForClass( - string $className, - ?Mapping\ComponentMetadata $parent, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) : Mapping\ComponentMetadata { - // IMPORTANT: We're handling $metadata as "parent" metadata here, while building the $className ClassMetadata. - $reflectionClass = new ReflectionClass($className); - - // Evaluate annotations on class metadata - $classAnnotations = $this->getClassAnnotations($reflectionClass); - $classMetadata = $this->convertClassAnnotationsToClassMetadata( - $classAnnotations, - $reflectionClass, - $parent, - $metadataBuildingContext - ); - - // Evaluate @Cache annotation - if (isset($classAnnotations[Annotation\Cache::class])) { - $cacheAnnot = $classAnnotations[Annotation\Cache::class]; - $cache = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata); - - $classMetadata->setCache($cache); - } - - // Evaluate annotations on properties/fields - /** @var ReflectionProperty $reflectionProperty */ - foreach ($reflectionClass->getProperties() as $reflectionProperty) { - if ($reflectionProperty->getDeclaringClass()->getClassName() !== $reflectionClass->getName()) { - continue; - } - - $propertyAnnotations = $this->getPropertyAnnotations($reflectionProperty); - $property = $this->convertReflectionPropertyAnnotationsToProperty( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - - $classMetadata->addProperty($property); - } - - return $classMetadata; - } - - /** - * {@inheritdoc} - */ - public function getAllClassNames() : array - { - if ($this->classNames !== null) { - return $this->classNames; - } - - $classNames = array_filter( - $this->locator->getAllClassNames(null), - function ($className) { - return ! $this->isTransient($className); - } - ); - - $this->classNames = $classNames; - - return $classNames; - } - - /** - * {@inheritdoc} - */ - public function isTransient($className) : bool - { - $reflectionClass = new ReflectionClass($className); - $classAnnotations = $this->reader->getClassAnnotations($reflectionClass); - - foreach ($classAnnotations as $annotation) { - if (isset(self::$entityAnnotationClasses[get_class($annotation)])) { - return false; - } - } - - return true; - } - - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @return Mapping\ClassMetadata|Mapping\ComponentMetadata - * - * @throws DBALException - * @throws ReflectionException - */ - private function convertClassAnnotationsToClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - ?Mapping\ComponentMetadata $parent, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) : Mapping\ComponentMetadata { - switch (true) { - case isset($classAnnotations[Annotation\Entity::class]): - return $this->convertClassAnnotationsToEntityClassMetadata( - $classAnnotations, - $reflectionClass, - $parent, - $metadataBuildingContext - ); - - break; - - case isset($classAnnotations[Annotation\MappedSuperclass::class]): - return $this->convertClassAnnotationsToMappedSuperClassMetadata( - $classAnnotations, - $reflectionClass, - $parent, - $metadataBuildingContext - ); - case isset($classAnnotations[Annotation\Embeddable::class]): - return $this->convertClassAnnotationsToEntityClassMetadata( - $classAnnotations, - $reflectionClass, - $parent, - $metadataBuildingContext - ); - default: - throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($reflectionClass->getName()); - } - } - - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @return Mapping\ClassMetadata - * - * @throws DBALException - * @throws ReflectionException - */ - private function convertClassAnnotationsToEntityClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - ?Mapping\ComponentMetadata $parent, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) { - /** @var Annotation\Entity $entityAnnot */ - $entityAnnot = $classAnnotations[Annotation\Entity::class]; - $classMetadata = new Mapping\ClassMetadata($reflectionClass->getName(), $parent); - - if ($entityAnnot->repositoryClass !== null) { - $classMetadata->setCustomRepositoryClassName($entityAnnot->repositoryClass); - } - - if ($entityAnnot->readOnly) { - $classMetadata->asReadOnly(); - } - - // Evaluate @Table annotation - if (isset($classAnnotations[Annotation\Table::class])) { - /** @var Annotation\Table $tableAnnot */ - $tableAnnot = $classAnnotations[Annotation\Table::class]; - $table = $this->convertTableAnnotationToTableMetadata($tableAnnot); - - $classMetadata->setTable($table); - } - - // Evaluate @ChangeTrackingPolicy annotation - if (isset($classAnnotations[Annotation\ChangeTrackingPolicy::class])) { - /** @var Annotation\ChangeTrackingPolicy $changeTrackingAnnot */ - $changeTrackingAnnot = $classAnnotations[Annotation\ChangeTrackingPolicy::class]; - - $classMetadata->setChangeTrackingPolicy( - constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingAnnot->value)) - ); - } - - // Evaluate @EntityListeners annotation - if (isset($classAnnotations[Annotation\EntityListeners::class])) { - /** @var Annotation\EntityListeners $entityListenersAnnot */ - $entityListenersAnnot = $classAnnotations[Annotation\EntityListeners::class]; - $eventMap = [ - Events::prePersist => Annotation\PrePersist::class, - Events::postPersist => Annotation\PostPersist::class, - Events::preUpdate => Annotation\PreUpdate::class, - Events::postUpdate => Annotation\PostUpdate::class, - Events::preRemove => Annotation\PreRemove::class, - Events::postRemove => Annotation\PostRemove::class, - Events::postLoad => Annotation\PostLoad::class, - Events::preFlush => Annotation\PreFlush::class, - ]; - - foreach ($entityListenersAnnot->value as $listenerClassName) { - if (! class_exists($listenerClassName)) { - throw Mapping\MappingException::entityListenerClassNotFound( - $listenerClassName, - $classMetadata->getClassName() - ); - } - - $listenerClass = new ReflectionClass($listenerClassName); - - /** @var ReflectionMethod $reflectionMethod */ - foreach ($listenerClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - $annotations = $this->getMethodAnnotations($reflectionMethod); - - foreach ($eventMap as $eventName => $annotationClassName) { - if (isset($annotations[$annotationClassName])) { - $classMetadata->addEntityListener($eventName, $listenerClassName, $reflectionMethod->getName()); - } - } - } - } - } - - // Evaluate @HasLifecycleCallbacks annotation - if (isset($classAnnotations[Annotation\HasLifecycleCallbacks::class])) { - $eventMap = [ - Events::prePersist => Annotation\PrePersist::class, - Events::postPersist => Annotation\PostPersist::class, - Events::preUpdate => Annotation\PreUpdate::class, - Events::postUpdate => Annotation\PostUpdate::class, - Events::preRemove => Annotation\PreRemove::class, - Events::postRemove => Annotation\PostRemove::class, - Events::postLoad => Annotation\PostLoad::class, - Events::preFlush => Annotation\PreFlush::class, - ]; - - /** @var ReflectionMethod $reflectionMethod */ - foreach ($reflectionClass->getMethods(ReflectionMethod::IS_PUBLIC) as $reflectionMethod) { - $annotations = $this->getMethodAnnotations($reflectionMethod); - - foreach ($eventMap as $eventName => $annotationClassName) { - if (isset($annotations[$annotationClassName])) { - $classMetadata->addLifecycleCallback($eventName, $reflectionMethod->getName()); - } - } - } - } - - // Evaluate @InheritanceType annotation - if (isset($classAnnotations[Annotation\InheritanceType::class])) { - /** @var Annotation\InheritanceType $inheritanceTypeAnnot */ - $inheritanceTypeAnnot = $classAnnotations[Annotation\InheritanceType::class]; - - $classMetadata->setInheritanceType( - constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceTypeAnnot->value)) - ); - - if ($classMetadata->inheritanceType !== Mapping\InheritanceType::NONE) { - $discriminatorColumn = new Mapping\DiscriminatorColumnMetadata(); - - // Evaluate @DiscriminatorColumn annotation - if (isset($classAnnotations[Annotation\DiscriminatorColumn::class])) { - /** @var Annotation\DiscriminatorColumn $discriminatorColumnAnnot */ - $discriminatorColumnAnnot = $classAnnotations[Annotation\DiscriminatorColumn::class]; - - $discriminatorColumn->setColumnName($discriminatorColumnAnnot->name); - - if (! empty($discriminatorColumnAnnot->columnDefinition)) { - $discriminatorColumn->setColumnDefinition($discriminatorColumnAnnot->columnDefinition); - } - - if (! empty($discriminatorColumnAnnot->type)) { - $discriminatorColumn->setType(Type::getType($discriminatorColumnAnnot->type)); - } - - if (! empty($discriminatorColumnAnnot->length)) { - $discriminatorColumn->setLength($discriminatorColumnAnnot->length); - } - } - - if (empty($discriminatorColumn->getColumnName())) { - throw Mapping\MappingException::nameIsMandatoryForDiscriminatorColumns($reflectionClass->getName()); - } - - $classMetadata->setDiscriminatorColumn($discriminatorColumn); - - // Evaluate @DiscriminatorMap annotation - if (isset($classAnnotations[Annotation\DiscriminatorMap::class])) { - /** @var Annotation\DiscriminatorMap $discriminatorMapAnnotation */ - $discriminatorMapAnnotation = $classAnnotations[Annotation\DiscriminatorMap::class]; - $discriminatorMap = $discriminatorMapAnnotation->value; - - $classMetadata->setDiscriminatorMap($discriminatorMap); - } - } - } - - return $classMetadata; - } - - /** - * @param Annotation\Annotation[] $classAnnotations - * - * @return Mapping\MappedSuperClassMetadata - */ - private function convertClassAnnotationsToMappedSuperClassMetadata( - array $classAnnotations, - ReflectionClass $reflectionClass, - ?Mapping\ComponentMetadata $parent, - Mapping\ClassMetadataBuildingContext $metadataBuildingContext - ) { - /** @var Annotation\MappedSuperclass $mappedSuperclassAnnot */ - $mappedSuperclassAnnot = $classAnnotations[Annotation\MappedSuperclass::class]; - $classMetadata = new Mapping\MappedSuperClassMetadata($reflectionClass->getName()); - - if ($mappedSuperclassAnnot->repositoryClass !== null) { - $classMetadata->setCustomRepositoryClassName($mappedSuperclassAnnot->repositoryClass); - } - - return $classMetadata; - } - - /** - * Parse the given Table as TableMetadata - * - * @return Mapping\TableMetadata - */ - private function convertTableAnnotationToTableMetadata(Annotation\Table $tableAnnot) - { - $table = new Mapping\TableMetadata(); - - if (! empty($tableAnnot->name)) { - $table->setName($tableAnnot->name); - } - - if (! empty($tableAnnot->schema)) { - $table->setSchema($tableAnnot->schema); - } - - foreach ($tableAnnot->options as $optionName => $optionValue) { - $table->addOption($optionName, $optionValue); - } - - foreach ($tableAnnot->indexes as $indexAnnot) { - $table->addIndex([ - 'name' => $indexAnnot->name, - 'columns' => $indexAnnot->columns, - 'unique' => $indexAnnot->unique, - 'options' => $indexAnnot->options, - 'flags' => $indexAnnot->flags, - ]); - } - - foreach ($tableAnnot->uniqueConstraints as $uniqueConstraintAnnot) { - $table->addUniqueConstraint([ - 'name' => $uniqueConstraintAnnot->name, - 'columns' => $uniqueConstraintAnnot->columns, - 'options' => $uniqueConstraintAnnot->options, - 'flags' => $uniqueConstraintAnnot->flags, - ]); - } - - return $table; - } - - /** - * Parse the given Cache as CacheMetadata - * - * @param string|null $fieldName - * - * @return Mapping\CacheMetadata - */ - private function convertCacheAnnotationToCacheMetadata( - Annotation\Cache $cacheAnnot, - Mapping\ClassMetadata $metadata, - $fieldName = null - ) { - $usage = constant(sprintf('%s::%s', Mapping\CacheUsage::class, $cacheAnnot->usage)); - $baseRegion = strtolower(str_replace('\\', '_', $metadata->getRootClassName())); - $defaultRegion = $baseRegion . ($fieldName ? '__' . $fieldName : ''); - - return new Mapping\CacheMetadata($usage, $cacheAnnot->region ?: $defaultRegion); - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\Property - * - * @throws Mapping\MappingException - */ - private function convertReflectionPropertyAnnotationsToProperty( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - // Field can only be annotated with one of: - // @Column, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany, @Embedded - switch (true) { - case isset($propertyAnnotations[Annotation\Column::class]): - return $this->convertReflectionPropertyToFieldMetadata( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - case isset($propertyAnnotations[Annotation\OneToOne::class]): - return $this->convertReflectionPropertyToOneToOneAssociationMetadata( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - case isset($propertyAnnotations[Annotation\ManyToOne::class]): - return $this->convertReflectionPropertyToManyToOneAssociationMetadata( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - case isset($propertyAnnotations[Annotation\OneToMany::class]): - return $this->convertReflectionPropertyToOneToManyAssociationMetadata( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - case isset($propertyAnnotations[Annotation\ManyToMany::class]): - return $this->convertReflectionPropertyToManyToManyAssociationMetadata( - $reflectionProperty, - $propertyAnnotations, - $classMetadata - ); - case isset($propertyAnnotations[Annotation\Embedded::class]): - // @todo guilhermeblanco Implement later... =) - break; - - default: - return new Mapping\TransientMetadata($reflectionProperty->getName()); - } - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\FieldMetadata - * - * @throws Mapping\MappingException - * @throws DBALException - */ - private function convertReflectionPropertyToFieldMetadata( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - $className = $classMetadata->getClassName(); - $fieldName = $reflectionProperty->getName(); - $columnAnnot = $propertyAnnotations[Annotation\Column::class]; - $isVersioned = isset($propertyAnnotations[Annotation\Version::class]); - $isPrimaryKey = isset($propertyAnnotations[Annotation\Id::class]); - - if ($columnAnnot->type === null) { - throw Mapping\MappingException::propertyTypeIsRequired($className, $fieldName); - } - - if ($isVersioned && $isPrimaryKey) { - throw Mapping\MappingException::cannotVersionIdField($className, $fieldName); - } - - $fieldType = Type::getType($columnAnnot->type); - $columnName = empty($columnAnnot->name) - ? $this->namingStrategy->propertyToColumnName($fieldName, $className) - : $columnAnnot->name; - - $fieldMetadata = new Mapping\FieldMetadata($fieldName); - - $fieldMetadata->setType($fieldType); - $fieldMetadata->setVersioned($isVersioned); - $fieldMetadata->setColumnName($columnName); - $fieldMetadata->setScale($columnAnnot->scale); - $fieldMetadata->setPrecision($columnAnnot->precision); - $fieldMetadata->setNullable($columnAnnot->nullable); - $fieldMetadata->setUnique($columnAnnot->unique); - - // Check for Id - if ($isPrimaryKey) { - if ($fieldType->canRequireSQLConversion()) { - throw Mapping\MappingException::sqlConversionNotAllowedForPrimaryKeyProperties( - $className, - $fieldName, - $fieldType->getName() - ); - } - - $fieldMetadata->setPrimaryKey(true); - } - - if (! empty($columnAnnot->columnDefinition)) { - $fieldMetadata->setColumnDefinition($columnAnnot->columnDefinition); - } - - if (! empty($columnAnnot->length)) { - $fieldMetadata->setLength($columnAnnot->length); - } - - // Assign default options - $customOptions = $columnAnnot->options ?? []; - $defaultOptions = []; - - if ($isVersioned) { - switch ($fieldMetadata->getTypeName()) { - case 'integer': - case 'bigint': - case 'smallint': - $defaultOptions['default'] = 1; - break; - - case 'datetime': - $defaultOptions['default'] = 'CURRENT_TIMESTAMP'; - break; - - default: - if (! isset($customOptions['default'])) { - throw Mapping\MappingException::unsupportedOptimisticLockingType($fieldMetadata->getType()); - } - } - } - - $fieldMetadata->setOptions(array_merge($defaultOptions, $customOptions)); - - return $fieldMetadata; - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\OneToOneAssociationMetadata - */ - private function convertReflectionPropertyToOneToOneAssociationMetadata( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - $className = $classMetadata->getClassName(); - $fieldName = $reflectionProperty->getName(); - $oneToOneAnnot = $propertyAnnotations[Annotation\OneToOne::class]; - - if ($oneToOneAnnot->targetEntity === null) { - throw Mapping\MappingException::missingTargetEntity($fieldName); - } - - $assocMetadata = new Mapping\OneToOneAssociationMetadata($fieldName); - $targetEntity = $oneToOneAnnot->targetEntity; - - $assocMetadata->setSourceEntity($className); - $assocMetadata->setTargetEntity($targetEntity); - $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToOneAnnot->cascade)); - $assocMetadata->setOrphanRemoval($oneToOneAnnot->orphanRemoval); - $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToOneAnnot->fetch)); - - if (! empty($oneToOneAnnot->mappedBy)) { - $assocMetadata->setMappedBy($oneToOneAnnot->mappedBy); - } - - if (! empty($oneToOneAnnot->inversedBy)) { - $assocMetadata->setInversedBy($oneToOneAnnot->inversedBy); - } - - // Check for Id - if (isset($propertyAnnotations[Annotation\Id::class])) { - $assocMetadata->setPrimaryKey(true); - } - - // Check for Cache - if (isset($propertyAnnotations[Annotation\Cache::class])) { - $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; - $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); - - $assocMetadata->setCache($cacheMetadata); - } - - // Check for JoinColumn/JoinColumns annotations - switch (true) { - case isset($propertyAnnotations[Annotation\JoinColumn::class]): - $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $assocMetadata->addJoinColumn($joinColumn); - - break; - - case isset($propertyAnnotations[Annotation\JoinColumns::class]): - $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; - - foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $assocMetadata->addJoinColumn($joinColumn); - } - - break; - } - - return $assocMetadata; - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\ManyToOneAssociationMetadata - */ - private function convertReflectionPropertyToManyToOneAssociationMetadata( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - $className = $classMetadata->getClassName(); - $fieldName = $reflectionProperty->getName(); - $manyToOneAnnot = $propertyAnnotations[Annotation\ManyToOne::class]; - - if ($manyToOneAnnot->targetEntity === null) { - throw Mapping\MappingException::missingTargetEntity($fieldName); - } - - $assocMetadata = new Mapping\ManyToOneAssociationMetadata($fieldName); - $targetEntity = $manyToOneAnnot->targetEntity; - - $assocMetadata->setSourceEntity($className); - $assocMetadata->setTargetEntity($targetEntity); - $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToOneAnnot->cascade)); - $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToOneAnnot->fetch)); - - if (! empty($manyToOneAnnot->inversedBy)) { - $assocMetadata->setInversedBy($manyToOneAnnot->inversedBy); - } - - // Check for Id - if (isset($propertyAnnotations[Annotation\Id::class])) { - $assocMetadata->setPrimaryKey(true); - } - - // Check for Cache - if (isset($propertyAnnotations[Annotation\Cache::class])) { - $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; - $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); - - $assocMetadata->setCache($cacheMetadata); - } - - // Check for JoinColumn/JoinColumns annotations - switch (true) { - case isset($propertyAnnotations[Annotation\JoinColumn::class]): - $joinColumnAnnot = $propertyAnnotations[Annotation\JoinColumn::class]; - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $assocMetadata->addJoinColumn($joinColumn); - - break; - - case isset($propertyAnnotations[Annotation\JoinColumns::class]): - $joinColumnsAnnot = $propertyAnnotations[Annotation\JoinColumns::class]; - - foreach ($joinColumnsAnnot->value as $joinColumnAnnot) { - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $assocMetadata->addJoinColumn($joinColumn); - } - - break; - } - - return $assocMetadata; - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\OneToManyAssociationMetadata - */ - private function convertReflectionPropertyToOneToManyAssociationMetadata( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - $className = $classMetadata->getClassName(); - $fieldName = $reflectionProperty->getName(); - $oneToManyAnnot = $propertyAnnotations[Annotation\OneToMany::class]; - - if ($oneToManyAnnot->targetEntity === null) { - throw Mapping\MappingException::missingTargetEntity($fieldName); - } - - $assocMetadata = new Mapping\OneToManyAssociationMetadata($fieldName); - $targetEntity = $oneToManyAnnot->targetEntity; - - $assocMetadata->setSourceEntity($className); - $assocMetadata->setTargetEntity($targetEntity); - $assocMetadata->setCascade($this->getCascade($className, $fieldName, $oneToManyAnnot->cascade)); - $assocMetadata->setOrphanRemoval($oneToManyAnnot->orphanRemoval); - $assocMetadata->setFetchMode($this->getFetchMode($className, $oneToManyAnnot->fetch)); - - if (! empty($oneToManyAnnot->mappedBy)) { - $assocMetadata->setMappedBy($oneToManyAnnot->mappedBy); - } - - if (! empty($oneToManyAnnot->indexBy)) { - $assocMetadata->setIndexedBy($oneToManyAnnot->indexBy); - } - - // Check for OrderBy - if (isset($propertyAnnotations[Annotation\OrderBy::class])) { - $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; - - $assocMetadata->setOrderBy($orderByAnnot->value); - } - - // Check for Id - if (isset($propertyAnnotations[Annotation\Id::class])) { - $assocMetadata->setPrimaryKey(true); - } - - // Check for Cache - if (isset($propertyAnnotations[Annotation\Cache::class])) { - $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; - $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); - - $assocMetadata->setCache($cacheMetadata); - } - - return $assocMetadata; - } - - /** - * @param Annotation\Annotation[] $propertyAnnotations - * - * @return Mapping\ManyToManyAssociationMetadata - */ - private function convertReflectionPropertyToManyToManyAssociationMetadata( - ReflectionProperty $reflectionProperty, - array $propertyAnnotations, - Mapping\ClassMetadata $classMetadata - ) { - $className = $classMetadata->getClassName(); - $fieldName = $reflectionProperty->getName(); - $manyToManyAnnot = $propertyAnnotations[Annotation\ManyToMany::class]; - - if ($manyToManyAnnot->targetEntity === null) { - throw Mapping\MappingException::missingTargetEntity($fieldName); - } - - $assocMetadata = new Mapping\ManyToManyAssociationMetadata($fieldName); - $targetEntity = $manyToManyAnnot->targetEntity; - - $assocMetadata->setSourceEntity($className); - $assocMetadata->setTargetEntity($targetEntity); - $assocMetadata->setCascade($this->getCascade($className, $fieldName, $manyToManyAnnot->cascade)); - $assocMetadata->setOrphanRemoval($manyToManyAnnot->orphanRemoval); - $assocMetadata->setFetchMode($this->getFetchMode($className, $manyToManyAnnot->fetch)); - - if (! empty($manyToManyAnnot->mappedBy)) { - $assocMetadata->setMappedBy($manyToManyAnnot->mappedBy); - } - - if (! empty($manyToManyAnnot->inversedBy)) { - $assocMetadata->setInversedBy($manyToManyAnnot->inversedBy); - } - - if (! empty($manyToManyAnnot->indexBy)) { - $assocMetadata->setIndexedBy($manyToManyAnnot->indexBy); - } - - // Check for JoinTable - if (isset($propertyAnnotations[Annotation\JoinTable::class])) { - $joinTableAnnot = $propertyAnnotations[Annotation\JoinTable::class]; - $joinTableMetadata = $this->convertJoinTableAnnotationToJoinTableMetadata( - $reflectionProperty, - $joinTableAnnot, - $classMetadata - ); - - $assocMetadata->setJoinTable($joinTableMetadata); - } - - // Check for OrderBy - if (isset($propertyAnnotations[Annotation\OrderBy::class])) { - $orderByAnnot = $propertyAnnotations[Annotation\OrderBy::class]; - - $assocMetadata->setOrderBy($orderByAnnot->value); - } - - // Check for Id - if (isset($propertyAnnotations[Annotation\Id::class])) { - $assocMetadata->setPrimaryKey(true); - } - - // Check for Cache - if (isset($propertyAnnotations[Annotation\Cache::class])) { - $cacheAnnot = $propertyAnnotations[Annotation\Cache::class]; - $cacheMetadata = $this->convertCacheAnnotationToCacheMetadata($cacheAnnot, $classMetadata, $fieldName); - - $assocMetadata->setCache($cacheMetadata); - } - - return $assocMetadata; - } - - /** - * Parse the given JoinTable as JoinTableMetadata - * - * @return Mapping\JoinTableMetadata - */ - private function convertJoinTableAnnotationToJoinTableMetadata( - ReflectionProperty $reflectionProperty, - Annotation\JoinTable $joinTableAnnot, - Mapping\ClassMetadata $classMetadata - ) { - $joinTable = new Mapping\JoinTableMetadata(); - - if (! empty($joinTableAnnot->name)) { - $joinTable->setName($joinTableAnnot->name); - } - - if (! empty($joinTableAnnot->schema)) { - $joinTable->setSchema($joinTableAnnot->schema); - } - - foreach ($joinTableAnnot->joinColumns as $joinColumnAnnot) { - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $joinTable->addJoinColumn($joinColumn); - } - - foreach ($joinTableAnnot->inverseJoinColumns as $joinColumnAnnot) { - $joinColumn = $this->convertJoinColumnAnnotationToJoinColumnMetadata( - $reflectionProperty, - $joinColumnAnnot, - $classMetadata - ); - - $joinTable->addInverseJoinColumn($joinColumn); - } - - return $joinTable; - } - - /** - * Parse the given JoinColumn as JoinColumnMetadata - * - * @return Mapping\JoinColumnMetadata - */ - private function convertJoinColumnAnnotationToJoinColumnMetadata( - ReflectionProperty $reflectionProperty, - Annotation\JoinColumn $joinColumnAnnot, - Mapping\ClassMetadata $classMetadata - ) { - $fieldName = $reflectionProperty->getName(); - $joinColumn = new Mapping\JoinColumnMetadata(); - $columnName = empty($joinColumnAnnot->name) - ? $this->namingStrategy->propertyToColumnName($fieldName, $classMetadata->getClassName()) - : $joinColumnAnnot->name; - $referencedColumnName = empty($joinColumnAnnot->referencedColumnName) - ? $this->namingStrategy->referenceColumnName() - : $joinColumnAnnot->referencedColumnName; - - $joinColumn->setColumnName($columnName); - $joinColumn->setReferencedColumnName($referencedColumnName); - $joinColumn->setNullable($joinColumnAnnot->nullable); - $joinColumn->setUnique($joinColumnAnnot->unique); - - if (! empty($joinColumnAnnot->fieldName)) { - $joinColumn->setAliasedName($joinColumnAnnot->fieldName); - } - - if (! empty($joinColumnAnnot->columnDefinition)) { - $joinColumn->setColumnDefinition($joinColumnAnnot->columnDefinition); - } - - if ($joinColumnAnnot->onDelete) { - $joinColumn->setOnDelete(strtoupper($joinColumnAnnot->onDelete)); - } - - return $joinColumn; - } - - /** - * Parses the given method. - * - * @return string[] - */ - private function getMethodCallbacks(ReflectionMethod $method) - { - $annotations = $this->getMethodAnnotations($method); - $events = [ - Events::prePersist => Annotation\PrePersist::class, - Events::postPersist => Annotation\PostPersist::class, - Events::preUpdate => Annotation\PreUpdate::class, - Events::postUpdate => Annotation\PostUpdate::class, - Events::preRemove => Annotation\PreRemove::class, - Events::postRemove => Annotation\PostRemove::class, - Events::postLoad => Annotation\PostLoad::class, - Events::preFlush => Annotation\PreFlush::class, - ]; - - // Check for callbacks - $callbacks = []; - - foreach ($events as $eventName => $annotationClassName) { - if (isset($annotations[$annotationClassName]) || $method->getName() === $eventName) { - $callbacks[] = $eventName; - } - } - - return $callbacks; - } - - /** - * Attempts to resolve the fetch mode. - * - * @param string $className The class name. - * @param string $fetchMode The fetch mode. - * - * @return int The fetch mode as defined in ClassMetadata. - * - * @throws Mapping\MappingException If the fetch mode is not valid. - */ - private function getFetchMode($className, $fetchMode) - { - $fetchModeConstant = sprintf('%s::%s', Mapping\FetchMode::class, $fetchMode); - - if (! defined($fetchModeConstant)) { - throw Mapping\MappingException::invalidFetchMode($className, $fetchMode); - } - - return constant($fetchModeConstant); - } - - /** - * @param string $className The class name. - * @param string $fieldName The field name. - * @param string[] $originalCascades The original unprocessed field cascades. - * - * @return string[] The processed field cascades. - * - * @throws Mapping\MappingException If a cascade option is not valid. - */ - private function getCascade(string $className, string $fieldName, array $originalCascades) - { - $cascadeTypes = ['remove', 'persist', 'refresh']; - $cascades = array_map('strtolower', $originalCascades); - - if (in_array('all', $cascades, true)) { - $cascades = $cascadeTypes; - } - - if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) { - $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes)); - - throw Mapping\MappingException::invalidCascadeOption($diffCascades, $className, $fieldName); - } - - return $cascades; - } - - /** - * @return Annotation\Annotation[] - */ - private function getClassAnnotations(ReflectionClass $reflectionClass) - { - $classAnnotations = $this->reader->getClassAnnotations($reflectionClass); - - foreach ($classAnnotations as $key => $annot) { - if (! is_numeric($key)) { - continue; - } - - $classAnnotations[get_class($annot)] = $annot; - } - - return $classAnnotations; - } - - /** - * @return Annotation\Annotation[] - */ - private function getPropertyAnnotations(ReflectionProperty $reflectionProperty) - { - $propertyAnnotations = $this->reader->getPropertyAnnotations($reflectionProperty); - - foreach ($propertyAnnotations as $key => $annot) { - if (! is_numeric($key)) { - continue; - } - - $propertyAnnotations[get_class($annot)] = $annot; - } - - return $propertyAnnotations; - } - - /** - * @return Annotation\Annotation[] - */ - private function getMethodAnnotations(ReflectionMethod $reflectionMethod) - { - $methodAnnotations = $this->reader->getMethodAnnotations($reflectionMethod); - - foreach ($methodAnnotations as $key => $annot) { - if (! is_numeric($key)) { - continue; - } - - $methodAnnotations[get_class($annot)] = $annot; - } - - return $methodAnnotations; - } -} diff --git a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php index 35ef7ed4d87..4866961ae24 100644 --- a/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ORM/Mapping/Driver/XmlDriver.php @@ -5,7 +5,6 @@ namespace Doctrine\ORM\Mapping\Driver; use Doctrine\Common\Collections\Criteria; -use Doctrine\DBAL\DBALException; use Doctrine\ORM\Annotation; use Doctrine\ORM\Events; use Doctrine\ORM\Mapping; @@ -18,7 +17,6 @@ use function file_get_contents; use function in_array; use function simplexml_load_string; -use function sprintf; use function str_replace; use function strtoupper; @@ -37,172 +35,83 @@ public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENS parent::__construct($locator, $fileExtension); } - /** - * {@inheritDoc} - * - * @throws DBALException - */ public function loadMetadataForClass( string $className, ?Mapping\ComponentMetadata $parent, Mapping\ClassMetadataBuildingContext $metadataBuildingContext ) : Mapping\ComponentMetadata { - $metadata = new Mapping\ClassMetadata($className, $parent); - /** @var SimpleXMLElement $xmlRoot */ - $xmlRoot = $this->getElement($className); - - if ($xmlRoot->getName() === 'entity') { - if (isset($xmlRoot['repository-class'])) { - $metadata->setCustomRepositoryClassName((string) $xmlRoot['repository-class']); - } - - if (isset($xmlRoot['read-only']) && $this->evaluateBoolean($xmlRoot['read-only'])) { - $metadata->asReadOnly(); - } - } elseif ($xmlRoot->getName() === 'mapped-superclass') { - if (isset($xmlRoot['repository-class'])) { - $metadata->setCustomRepositoryClassName((string) $xmlRoot['repository-class']); - } - - $metadata->isMappedSuperclass = true; - } elseif ($xmlRoot->getName() === 'embeddable') { - $metadata->isEmbeddedClass = true; - } else { - throw Mapping\MappingException::classIsNotAValidEntityOrMappedSuperClass($className); - } - - // Process table information - $parent = $metadata->getParent(); - - if ($parent && $parent->inheritanceType === Mapping\InheritanceType::SINGLE_TABLE) { - // Handle the case where a middle mapped super class inherits from a single table inheritance tree. - do { - if (! $parent->isMappedSuperclass) { - $metadata->setTable($parent->table); - - break; - } - - $parent = $parent->getParent(); - } while ($parent !== null); - } else { - $tableAnnotation = new Annotation\Table(); - - // Evaluate attributes - if (isset($xmlRoot['table'])) { - $tableAnnotation->name = (string) $xmlRoot['table']; - } - - if (isset($xmlRoot['schema'])) { - $tableAnnotation->schema = (string) $xmlRoot['schema']; - } - - // Evaluate - if (isset($xmlRoot->indexes)) { - $tableAnnotation->indexes = $this->parseIndexes($xmlRoot->indexes->children()); - } - - // Evaluate - if (isset($xmlRoot->{'unique-constraints'})) { - $tableAnnotation->uniqueConstraints = $this->parseUniqueConstraints($xmlRoot->{'unique-constraints'}->children()); - } - - if (isset($xmlRoot->options)) { - $tableAnnotation->options = $this->parseOptions($xmlRoot->options->children()); - } - - $tableBuilder = new Builder\TableMetadataBuilder($metadataBuildingContext); - - $tableBuilder - ->withEntityClassMetadata($metadata) - ->withTableAnnotation($tableAnnotation); - - $metadata->setTable($tableBuilder->build()); - } - - // Evaluate second level cache - if (isset($xmlRoot->cache)) { - $cacheBuilder = new Builder\CacheMetadataBuilder($metadataBuildingContext); - - $cacheBuilder - ->withComponentMetadata($metadata) - ->withCacheAnnotation($this->convertCacheElementToCacheAnnotation($xmlRoot->cache)); - - $metadata->setCache($cacheBuilder->build()); - } - - if (isset($xmlRoot['inheritance-type'])) { - $inheritanceType = strtoupper((string) $xmlRoot['inheritance-type']); - - $metadata->setInheritanceType( - constant(sprintf('%s::%s', Mapping\InheritanceType::class, $inheritanceType)) - ); - - if ($metadata->inheritanceType !== Mapping\InheritanceType::NONE) { - $discriminatorColumnBuilder = new Builder\DiscriminatorColumnMetadataBuilder($metadataBuildingContext); - - $discriminatorColumnBuilder - ->withComponentMetadata($metadata) - ->withDiscriminatorColumnAnnotation( - isset($xmlRoot->{'discriminator-column'}) - ? $this->convertDiscrimininatorColumnElementToDiscriminatorColumnAnnotation($xmlRoot->{'discriminator-column'}) - : null - ); - - $metadata->setDiscriminatorColumn($discriminatorColumnBuilder->build()); - - // Evaluate - if (isset($xmlRoot->{'discriminator-map'})) { - $map = []; - - foreach ($xmlRoot->{'discriminator-map'}->{'discriminator-mapping'} as $discrMapElement) { - $map[(string) $discrMapElement['value']] = (string) $discrMapElement['class']; - } - - $metadata->setDiscriminatorMap($map); - } - } - } - - // Evaluate - if (isset($xmlRoot['change-tracking-policy'])) { - $changeTrackingPolicy = strtoupper((string) $xmlRoot['change-tracking-policy']); - - $metadata->setChangeTrackingPolicy( - constant(sprintf('%s::%s', Mapping\ChangeTrackingPolicy::class, $changeTrackingPolicy)) - ); - } + $xmlRoot = $this->getElement($className); + $classBuilder = new Builder\ClassMetadataBuilder($metadataBuildingContext); + $classMetadata = $classBuilder + ->withClassName($className) + ->withParentMetadata($parent) + ->withEntityAnnotation( + $xmlRoot->getName() === 'entity' + ? $this->convertEntityElementToEntityAnnotation($xmlRoot) + : null + ) + ->withMappedSuperclassAnnotation( + $xmlRoot->getName() === 'mapped-superclass' + ? $this->convertMappedSuperclassElementToMappedSuperclassAnnotation($xmlRoot) + : null + ) + ->withEmbeddableAnnotation( + $xmlRoot->getName() === 'embeddable' + ? null + : null + ) + ->withTableAnnotation( + // @todo guilhermeblanco Is this the proper check to build Table annotation? + $xmlRoot->getName() === 'entity' + ? $this->convertTableElementToTableAnnotation($xmlRoot) + : null + ) + ->withInheritanceTypeAnnotation( + isset($xmlRoot['inheritance-type']) + ? $this->convertInheritanceTypeElementToInheritanceTypeAnnotation($xmlRoot) + : null + ) + ->withDiscriminatorColumnAnnotation( + isset($xmlRoot->{'discriminator-column'}) + ? $this->convertDiscrimininatorColumnElementToDiscriminatorColumnAnnotation($xmlRoot->{'discriminator-column'}) + : null + ) + ->withDiscriminatorMapAnnotation( + isset($xmlRoot->{'discriminator-map'}) + ? $this->convertDiscriminatorMapElementToDiscriminatorMapAnnotation($xmlRoot->{'discriminator-map'}) + : null + ) + ->withChangeTrackingPolicyAnnotation( + isset($xmlRoot['change-tracking-policy']) + ? $this->convertChangeTrackingPolicyElementToChangeTrackingPolicyAnnotation($xmlRoot) + : null + ) + ->withCacheAnnotation( + isset($xmlRoot->cache) + ? $this->convertCacheElementToCacheAnnotation($xmlRoot->cache) + : null + ) + ->build(); + + $propertyBuilder = new Builder\PropertyMetadataBuilder($metadataBuildingContext); + + $propertyBuilder->withComponentMetadata($classMetadata); // Evaluate mappings if (isset($xmlRoot->field)) { - $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext); - - $fieldBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->field as $fieldElement) { - $versionAnnotation = isset($fieldElement['version']) && $this->evaluateBoolean($fieldElement['version']) - ? new Annotation\Version() - : null; - - $fieldBuilder + $propertyBuilder ->withFieldName((string) $fieldElement['name']) ->withColumnAnnotation($this->convertFieldElementToColumnAnnotation($fieldElement)) ->withIdAnnotation(null) - ->withVersionAnnotation($versionAnnotation); - - $fieldMetadata = $fieldBuilder->build(); - - // Prevent column duplication - if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) { - throw Mapping\MappingException::duplicateColumnName( - $metadata->getClassName(), - $fieldMetadata->getColumnName() + ->withVersionAnnotation( + isset($fieldElement['version']) && $this->evaluateBoolean($fieldElement['version']) + ? new Annotation\Version() + : null ); - } - $metadata->addProperty($fieldMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } } @@ -222,18 +131,13 @@ public function loadMetadataForClass( 'columnPrefix' => $useColumnPrefix ? $columnPrefix : false, ]; - $metadata->mapEmbedded($mapping); + $classMetadata->mapEmbedded($mapping); } } // Evaluate mappings $associationIds = []; - $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext); - - $fieldBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->id as $idElement) { $fieldName = (string) $idElement['name']; @@ -243,15 +147,15 @@ public function loadMetadataForClass( continue; } - $versionAnnotation = isset($idElement['version']) && $this->evaluateBoolean($idElement['version']) - ? new Annotation\Version() - : null; - - $fieldMetadata = $fieldBuilder + $propertyBuilder ->withFieldName($fieldName) ->withColumnAnnotation($this->convertFieldElementToColumnAnnotation($idElement)) ->withIdAnnotation(new Annotation\Id()) - ->withVersionAnnotation($versionAnnotation) + ->withVersionAnnotation( + isset($idElement['version']) && $this->evaluateBoolean($idElement['version']) + ? new Annotation\Version() + : null + ) ->withGeneratedValueAnnotation( isset($idElement->generator) ? $this->convertGeneratorElementToGeneratedValueAnnotation($idElement->generator) @@ -266,40 +170,26 @@ public function loadMetadataForClass( isset($idElement->{'custom-id-generator'}) ? $this->convertCustomIdGeneratorElementToCustomIdGeneratorAnnotation($idElement->{'custom-id-generator'}) : null - ) - ->build(); - - // Prevent column duplication - if ($metadata->checkPropertyDuplication($fieldMetadata->getColumnName())) { - throw Mapping\MappingException::duplicateColumnName( - $metadata->getClassName(), - $fieldMetadata->getColumnName() ); - } - $metadata->fieldNames[$fieldMetadata->getColumnName()] = $fieldMetadata->getName(); - - $metadata->addProperty($fieldMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } // Evaluate mappings if (isset($xmlRoot->{'one-to-one'})) { - $oneToOneAssociationBuilder = new Builder\OneToOneAssociationMetadataBuilder($metadataBuildingContext); - - $oneToOneAssociationBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->{'one-to-one'} as $oneToOneElement) { - $fieldName = (string) $oneToOneElement['field']; - $associationMetadata = $oneToOneAssociationBuilder + $fieldName = (string) $oneToOneElement['field']; + + $propertyBuilder ->withFieldName($fieldName) + ->withOneToOneAnnotation($this->convertOneToOneElementToOneToOneAnnotation($oneToOneElement)) + ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) + ->withVersionAnnotation(null) ->withCacheAnnotation( isset($oneToOneElement->cache) ? $this->convertCacheElementToCacheAnnotation($oneToOneElement->cache) : null ) - ->withOneToOneAnnotation($this->convertOneToOneElementToOneToOneAnnotation($oneToOneElement)) - ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) ->withJoinColumnAnnotation( isset($oneToOneElement->{'join-column'}) ? $this->convertJoinColumnElementToJoinColumnAnnotation($oneToOneElement->{'join-column'}) @@ -309,45 +199,27 @@ public function loadMetadataForClass( isset($oneToOneElement->{'join-columns'}) ? $this->convertJoinColumnsElementToJoinColumnsAnnotation($oneToOneElement->{'join-columns'}) : null - ) - ->build(); - - // Prevent column duplication - foreach ($associationMetadata->getJoinColumns() as $joinColumnMetadata) { - $columnName = $joinColumnMetadata->getColumnName(); - - // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. - //if ($metadata->checkPropertyDuplication($columnName)) { - // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); - //} - - if ($associationMetadata->isOwningSide()) { - $metadata->fieldNames[$columnName] = $associationMetadata->getName(); - } - } + ); - $metadata->addProperty($associationMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } } // Evaluate mappings if (isset($xmlRoot->{'many-to-one'})) { - $manyToOneAssociationBuilder = new Builder\ManyToOneAssociationMetadataBuilder($metadataBuildingContext); - - $manyToOneAssociationBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->{'many-to-one'} as $manyToOneElement) { - $fieldName = (string) $manyToOneElement['field']; - $associationMetadata = $manyToOneAssociationBuilder + $fieldName = (string) $manyToOneElement['field']; + + $propertyBuilder ->withFieldName($fieldName) + ->withManyToOneAnnotation($this->convertManyToOneElementToManyToOneAnnotation($manyToOneElement)) + ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) + ->withVersionAnnotation(null) ->withCacheAnnotation( isset($manyToOneElement->cache) ? $this->convertCacheElementToCacheAnnotation($manyToOneElement->cache) : null ) - ->withManyToOneAnnotation($this->convertManyToOneElementToManyToOneAnnotation($manyToOneElement)) - ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) ->withJoinColumnAnnotation( isset($manyToOneElement->{'join-column'}) ? $this->convertJoinColumnElementToJoinColumnAnnotation($manyToOneElement->{'join-column'}) @@ -357,73 +229,52 @@ public function loadMetadataForClass( isset($manyToOneElement->{'join-columns'}) ? $this->convertJoinColumnsElementToJoinColumnsAnnotation($manyToOneElement->{'join-columns'}) : null - ) - ->build(); - - // Prevent column duplication - foreach ($associationMetadata->getJoinColumns() as $joinColumnMetadata) { - $columnName = $joinColumnMetadata->getColumnName(); - - // @todo guilhermeblanco Open an issue to discuss making this scenario impossible. - //if ($metadata->checkPropertyDuplication($columnName)) { - // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); - //} - - if ($associationMetadata->isOwningSide()) { - $metadata->fieldNames[$columnName] = $associationMetadata->getName(); - } - } + ); - $metadata->addProperty($associationMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } } // Evaluate mappings if (isset($xmlRoot->{'one-to-many'})) { - $oneToManyAssociationBuilder = new Builder\OneToManyAssociationMetadataBuilder($metadataBuildingContext); - - $oneToManyAssociationBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->{'one-to-many'} as $oneToManyElement) { - $fieldName = (string) $oneToManyElement['field']; - $associationMetadata = $oneToManyAssociationBuilder + $fieldName = (string) $oneToManyElement['field']; + + $propertyBuilder ->withFieldName($fieldName) + ->withOneToManyAnnotation($this->convertOneToManyElementToOneToManyAnnotation($oneToManyElement)) + ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) + ->withVersionAnnotation(null) ->withCacheAnnotation( isset($oneToManyElement->cache) ? $this->convertCacheElementToCacheAnnotation($oneToManyElement->cache) : null ) - ->withOneToManyAnnotation($this->convertOneToManyElementToOneToManyAnnotation($oneToManyElement)) ->withOrderByAnnotation( isset($oneToManyElement->{'order-by'}) ? $this->convertOrderByElementToOrderByAnnotation($oneToManyElement->{'order-by'}) : null - ) - ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) - ->build(); + ); - $metadata->addProperty($associationMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } } // Evaluate mappings if (isset($xmlRoot->{'many-to-many'})) { - $manyToManyAssociationBuilder = new Builder\ManyToManyAssociationMetadataBuilder($metadataBuildingContext); - - $manyToManyAssociationBuilder - ->withComponentMetadata($metadata); - foreach ($xmlRoot->{'many-to-many'} as $manyToManyElement) { - $fieldName = (string) $manyToManyElement['field']; - $associationMetadata = $manyToManyAssociationBuilder + $fieldName = (string) $manyToManyElement['field']; + + $propertyBuilder ->withFieldName($fieldName) + ->withManyToManyAnnotation($this->convertManyToManyElementToManyToManyAnnotation($manyToManyElement)) + ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) + ->withVersionAnnotation(null) ->withCacheAnnotation( isset($manyToManyElement->cache) ? $this->convertCacheElementToCacheAnnotation($manyToManyElement->cache) : null ) - ->withManyToManyAnnotation($this->convertManyToManyElementToManyToManyAnnotation($manyToManyElement)) ->withJoinTableAnnotation( isset($manyToManyElement->{'join-table'}) ? $this->convertJoinTableElementToJoinTableAnnotation($manyToManyElement->{'join-table'}) @@ -433,11 +284,9 @@ public function loadMetadataForClass( isset($manyToManyElement->{'order-by'}) ? $this->convertOrderByElementToOrderByAnnotation($manyToManyElement->{'order-by'}) : null - ) - ->withIdAnnotation(isset($associationIds[$fieldName]) ? new Annotation\Id() : null) - ->build(); + ); - $metadata->addProperty($associationMetadata); + $classMetadata->addProperty($propertyBuilder->build()); } } @@ -446,14 +295,14 @@ public function loadMetadataForClass( $fieldBuilder = new Builder\FieldMetadataBuilder($metadataBuildingContext); $fieldBuilder - ->withComponentMetadata($metadata); + ->withComponentMetadata($classMetadata); foreach ($xmlRoot->{'attribute-overrides'}->{'attribute-override'} as $overrideElement) { $fieldName = (string) $overrideElement['name']; - $property = $metadata->getProperty($fieldName); + $property = $classMetadata->getProperty($fieldName); if (! $property) { - throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); + throw Mapping\MappingException::invalidOverrideFieldName($classMetadata->getClassName(), $fieldName); } foreach ($overrideElement->field as $fieldElement) { @@ -471,13 +320,11 @@ public function loadMetadataForClass( $columnName = $fieldMetadata->getColumnName(); // Prevent column duplication - if ($metadata->checkPropertyDuplication($columnName)) { - throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); + if ($classMetadata->checkPropertyDuplication($columnName)) { + throw Mapping\MappingException::duplicateColumnName($classMetadata->getClassName(), $columnName); } - $metadata->fieldNames[$fieldMetadata->getColumnName()] = $fieldName; - - $metadata->setPropertyOverride($fieldMetadata); + $classMetadata->setPropertyOverride($fieldMetadata); } } } @@ -486,10 +333,10 @@ public function loadMetadataForClass( if (isset($xmlRoot->{'association-overrides'})) { foreach ($xmlRoot->{'association-overrides'}->{'association-override'} as $overrideElement) { $fieldName = (string) $overrideElement['name']; - $property = $metadata->getProperty($fieldName); + $property = $classMetadata->getProperty($fieldName); if (! $property) { - throw Mapping\MappingException::invalidOverrideFieldName($metadata->getClassName(), $fieldName); + throw Mapping\MappingException::invalidOverrideFieldName($classMetadata->getClassName(), $fieldName); } $override = clone $property; @@ -499,7 +346,7 @@ public function loadMetadataForClass( $joinColumnBuilder = new Builder\JoinColumnMetadataBuilder($metadataBuildingContext); $joinColumnBuilder - ->withComponentMetadata($metadata) + ->withComponentMetadata($classMetadata) ->withFieldName($override->getName()); $joinColumns = []; @@ -517,10 +364,6 @@ public function loadMetadataForClass( // throw Mapping\MappingException::duplicateColumnName($metadata->getClassName(), $columnName); //} - if ($override->isOwningSide()) { - $metadata->fieldNames[$columnName] = $fieldName; - } - $joinColumns[] = $joinColumnMetadata; } @@ -534,7 +377,7 @@ public function loadMetadataForClass( $joinTableBuilder = new Builder\JoinTableMetadataBuilder($metadataBuildingContext); $joinTableBuilder - ->withComponentMetadata($metadata) + ->withComponentMetadata($classMetadata) ->withFieldName($property->getName()) ->withTargetEntity($property->getTargetEntity()) ->withJoinTableAnnotation($joinTableAnnotation); @@ -554,7 +397,7 @@ public function loadMetadataForClass( ); } - $metadata->setPropertyOverride($override); + $classMetadata->setPropertyOverride($override); } } @@ -564,7 +407,7 @@ public function loadMetadataForClass( $eventName = constant(Events::class . '::' . (string) $lifecycleCallback['type']); $methodName = (string) $lifecycleCallback['method']; - $metadata->addLifecycleCallback($eventName, $methodName); + $classMetadata->addLifecycleCallback($eventName, $methodName); } } @@ -576,7 +419,7 @@ public function loadMetadataForClass( if (! class_exists($listenerClassName)) { throw Mapping\MappingException::entityListenerClassNotFound( $listenerClassName, - $metadata->getClassName() + $classMetadata->getClassName() ); } @@ -584,109 +427,261 @@ public function loadMetadataForClass( $eventName = (string) $callbackElement['type']; $methodName = (string) $callbackElement['method']; - $metadata->addEntityListener($eventName, $listenerClassName, $methodName); + $classMetadata->addEntityListener($eventName, $listenerClassName, $methodName); } } } - return $metadata; + return $classMetadata; } /** - * Parses (nested) index elements. - * - * @param SimpleXMLElement $indexes The XML element. - * - * @return Annotation\Index[] The indexes array. + * {@inheritDoc} */ - private function parseIndexes(SimpleXMLElement $indexes) : array + protected function loadMappingFile($file) { - $array = []; + $result = []; + // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577 + $xmlElement = simplexml_load_string(file_get_contents($file)); - /** @var SimpleXMLElement $index */ - foreach ($indexes as $index) { - $indexAnnotation = new Annotation\Index(); + if (isset($xmlElement->entity)) { + foreach ($xmlElement->entity as $entityElement) { + $entityName = (string) $entityElement['name']; + $result[$entityName] = $entityElement; + } + } elseif (isset($xmlElement->{'mapped-superclass'})) { + foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { + $className = (string) $mappedSuperClass['name']; + $result[$className] = $mappedSuperClass; + } + } elseif (isset($xmlElement->embeddable)) { + foreach ($xmlElement->embeddable as $embeddableElement) { + $embeddableName = (string) $embeddableElement['name']; + $result[$embeddableName] = $embeddableElement; + } + } - $indexAnnotation->columns = explode(',', (string) $index['columns']); - $indexAnnotation->options = isset($index->options) ? $this->parseOptions($index->options->children()) : []; - $indexAnnotation->flags = isset($index['flags']) ? explode(',', (string) $index['flags']) : []; + return $result; + } + + private function convertEntityElementToEntityAnnotation( + SimpleXMLElement $entityElement + ) : Annotation\Entity { + $entityAnnotation = new Annotation\Entity(); + + if (isset($entityElement['repository-class'])) { + $entityAnnotation->repositoryClass = (string) $entityElement['repository-class']; + } - if (isset($index['name'])) { - $indexAnnotation->name = (string) $index['name']; + if (isset($entityElement['read-only'])) { + $entityAnnotation->readOnly = $this->evaluateBoolean($entityElement['read-only']); + } + + return $entityAnnotation; + } + + private function convertMappedSuperclassElementToMappedSuperclassAnnotation( + SimpleXMLElement $mappedSuperclassElement + ) : Annotation\MappedSuperclass { + $mappedSuperclassAnnotation = new Annotation\MappedSuperclass(); + + if (isset($mappedSuperclassElement['repository-class'])) { + $mappedSuperclassAnnotation->repositoryClass = (string) $mappedSuperclassElement['repository-class']; + } + + return $mappedSuperclassAnnotation; + } + + private function convertTableElementToTableAnnotation( + SimpleXMLElement $tableElement + ) : Annotation\Table { + $tableAnnotation = new Annotation\Table(); + + // Evaluate attributes + if (isset($tableElement['table'])) { + $tableAnnotation->name = (string) $tableElement['table']; + } + + if (isset($tableElement['schema'])) { + $tableAnnotation->schema = (string) $tableElement['schema']; + } + + // Evaluate + if (isset($tableElement->indexes)) { + $indexes = []; + + /** @var SimpleXMLElement $indexElement */ + foreach ($tableElement->indexes->children() as $indexElement) { + $indexes[] = $this->convertIndexElementToIndexAnnotation($indexElement); } - if (isset($index['unique'])) { - $indexAnnotation->unique = $this->evaluateBoolean($index['unique']); + $tableAnnotation->indexes = $indexes; + } + + // Evaluate + if (isset($tableElement->{'unique-constraints'})) { + $uniqueConstraints = []; + + foreach ($tableElement->{'unique-constraints'}->children() as $uniqueConstraintElement) { + $uniqueConstraints[] = $this->convertUniqueConstraintElementToUniqueConstraintAnnotation($uniqueConstraintElement); } - $array[] = $indexAnnotation; + $tableAnnotation->uniqueConstraints = $uniqueConstraints; } - return $array; + if (isset($tableElement->options)) { + $tableAnnotation->options = $this->parseOptions($tableElement->options->children()); + } + + return $tableAnnotation; } - /** - * Parses (nested) unique constraint elements. - * - * @param SimpleXMLElement $uniqueConstraints The XML element. - * - * @return Annotation\UniqueConstraint[] The unique constraints array. - */ - private function parseUniqueConstraints(SimpleXMLElement $uniqueConstraints) : array - { - $array = []; + private function convertIndexElementToIndexAnnotation( + SimpleXMLElement $indexElement + ) : Annotation\Index { + $indexAnnotation = new Annotation\Index(); - /** @var SimpleXMLElement $uniqueConstraint */ - foreach ($uniqueConstraints as $uniqueConstraint) { - $uniqueConstraintAnnotation = new Annotation\UniqueConstraint(); + $indexAnnotation->columns = explode(',', (string) $indexElement['columns']); + $indexAnnotation->options = isset($indexElement->options) ? $this->parseOptions($indexElement->options->children()) : []; + $indexAnnotation->flags = isset($indexElement['flags']) ? explode(',', (string) $indexElement['flags']) : []; - $uniqueConstraintAnnotation->columns = explode(',', (string) $uniqueConstraint['columns']); - $uniqueConstraintAnnotation->options = isset($uniqueConstraint->options) ? $this->parseOptions($uniqueConstraint->options->children()) : []; - $uniqueConstraintAnnotation->flags = isset($uniqueConstraint['flags']) ? explode(',', (string) $uniqueConstraint['flags']) : []; + if (isset($indexElement['name'])) { + $indexAnnotation->name = (string) $indexElement['name']; + } - if (isset($uniqueConstraint['name'])) { - $uniqueConstraintAnnotation->name = (string) $uniqueConstraint['name']; - } + if (isset($indexElement['unique'])) { + $indexAnnotation->unique = $this->evaluateBoolean($indexElement['unique']); + } + + return $indexAnnotation; + } + + private function convertUniqueConstraintElementToUniqueConstraintAnnotation( + SimpleXMLElement $uniqueConstraintElement + ) : Annotation\UniqueConstraint { + $uniqueConstraintAnnotation = new Annotation\UniqueConstraint(); - $array[] = $uniqueConstraintAnnotation; + $uniqueConstraintAnnotation->columns = explode(',', (string) $uniqueConstraintElement['columns']); + $uniqueConstraintAnnotation->options = isset($uniqueConstraintElement->options) ? $this->parseOptions($uniqueConstraintElement->options->children()) : []; + $uniqueConstraintAnnotation->flags = isset($uniqueConstraintElement['flags']) ? explode(',', (string) $uniqueConstraintElement['flags']) : []; + + if (isset($uniqueConstraintElement['name'])) { + $uniqueConstraintAnnotation->name = (string) $uniqueConstraintElement['name']; } - return $array; + return $uniqueConstraintAnnotation; } - /** - * Parses (nested) option elements. - * - * @param SimpleXMLElement $options The XML element. - * - * @return mixed[] The options array. - */ - private function parseOptions(SimpleXMLElement $options) : array - { - $array = []; + private function convertInheritanceTypeElementToInheritanceTypeAnnotation( + SimpleXMLElement $inheritanceTypeElement + ) : Annotation\InheritanceType { + $inheritanceTypeAnnotation = new Annotation\InheritanceType(); - /** @var SimpleXMLElement $option */ - foreach ($options as $option) { - if ($option->count()) { - $value = $this->parseOptions($option->children()); - } else { - $value = (string) $option; - } + $inheritanceTypeAnnotation->value = strtoupper((string) $inheritanceTypeElement['inheritance-type']); - $attributes = $option->attributes(); + return $inheritanceTypeAnnotation; + } - if (isset($attributes->name)) { - $nameAttribute = (string) $attributes->name; + private function convertChangeTrackingPolicyElementToChangeTrackingPolicyAnnotation( + SimpleXMLElement $changeTrackingPolicyElement + ) : Annotation\ChangeTrackingPolicy { + $changeTrackingPolicyAnnotation = new Annotation\ChangeTrackingPolicy(); - $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'], true) - ? $this->evaluateBoolean($value) - : $value; - } else { - $array[] = $value; - } + $changeTrackingPolicyAnnotation->value = strtoupper((string) $changeTrackingPolicyElement['change-tracking-policy']); + + return $changeTrackingPolicyAnnotation; + } + + private function convertDiscrimininatorColumnElementToDiscriminatorColumnAnnotation( + SimpleXMLElement $discriminatorColumnElement + ) : Annotation\DiscriminatorColumn { + $discriminatorColumnAnnotation = new Annotation\DiscriminatorColumn(); + + $discriminatorColumnAnnotation->type = (string) ($discriminatorColumnElement['type'] ?? 'string'); + $discriminatorColumnAnnotation->name = (string) $discriminatorColumnElement['name']; + + if (isset($discriminatorColumnElement['column-definition'])) { + $discriminatorColumnAnnotation->columnDefinition = (string) $discriminatorColumnElement['column-definition']; } - return $array; + if (isset($discriminatorColumnElement['length'])) { + $discriminatorColumnAnnotation->length = (int) $discriminatorColumnElement['length']; + } + + return $discriminatorColumnAnnotation; + } + + private function convertDiscriminatorMapElementToDiscriminatorMapAnnotation( + SimpleXMLElement $discriminatorMapElement + ) : Annotation\DiscriminatorMap { + $discriminatorMapAnnotation = new Annotation\DiscriminatorMap(); + $discriminatorMap = []; + + foreach ($discriminatorMapElement->{'discriminator-mapping'} as $discriminatorMapElement) { + $discriminatorMap[(string) $discriminatorMapElement['value']] = (string) $discriminatorMapElement['class']; + } + + $discriminatorMapAnnotation->value = $discriminatorMap; + + return $discriminatorMapAnnotation; + } + + private function convertCacheElementToCacheAnnotation( + SimpleXMLElement $cacheElement + ) : Annotation\Cache { + $cacheAnnotation = new Annotation\Cache(); + + if (isset($cacheElement['region'])) { + $cacheAnnotation->region = (string) $cacheElement['region']; + } + + if (isset($cacheElement['usage'])) { + $cacheAnnotation->usage = strtoupper((string) $cacheElement['usage']); + } + + return $cacheAnnotation; + } + + private function convertFieldElementToColumnAnnotation( + SimpleXMLElement $fieldElement + ) : Annotation\Column { + $columnAnnotation = new Annotation\Column(); + + $columnAnnotation->type = isset($fieldElement['type']) ? (string) $fieldElement['type'] : 'string'; + + if (isset($fieldElement['column'])) { + $columnAnnotation->name = (string) $fieldElement['column']; + } + + if (isset($fieldElement['length'])) { + $columnAnnotation->length = (int) $fieldElement['length']; + } + + if (isset($fieldElement['precision'])) { + $columnAnnotation->precision = (int) $fieldElement['precision']; + } + + if (isset($fieldElement['scale'])) { + $columnAnnotation->scale = (int) $fieldElement['scale']; + } + + if (isset($fieldElement['unique'])) { + $columnAnnotation->unique = $this->evaluateBoolean($fieldElement['unique']); + } + + if (isset($fieldElement['nullable'])) { + $columnAnnotation->nullable = $this->evaluateBoolean($fieldElement['nullable']); + } + + if (isset($fieldElement['column-definition'])) { + $columnAnnotation->columnDefinition = (string) $fieldElement['column-definition']; + } + + if (isset($fieldElement->options)) { + $columnAnnotation->options = $this->parseOptions($fieldElement->options->children()); + } + + return $columnAnnotation; } private function convertOneToOneElementToOneToOneAnnotation( @@ -809,80 +804,6 @@ private function convertManyToManyElementToManyToManyAnnotation( return $manyToManyAnnotation; } - private function convertFieldElementToColumnAnnotation( - SimpleXMLElement $fieldElement - ) : Annotation\Column { - $columnAnnotation = new Annotation\Column(); - - $columnAnnotation->type = isset($fieldElement['type']) ? (string) $fieldElement['type'] : 'string'; - - if (isset($fieldElement['column'])) { - $columnAnnotation->name = (string) $fieldElement['column']; - } - - if (isset($fieldElement['length'])) { - $columnAnnotation->length = (int) $fieldElement['length']; - } - - if (isset($fieldElement['precision'])) { - $columnAnnotation->precision = (int) $fieldElement['precision']; - } - - if (isset($fieldElement['scale'])) { - $columnAnnotation->scale = (int) $fieldElement['scale']; - } - - if (isset($fieldElement['unique'])) { - $columnAnnotation->unique = $this->evaluateBoolean($fieldElement['unique']); - } - - if (isset($fieldElement['nullable'])) { - $columnAnnotation->nullable = $this->evaluateBoolean($fieldElement['nullable']); - } - - if (isset($fieldElement['column-definition'])) { - $columnAnnotation->columnDefinition = (string) $fieldElement['column-definition']; - } - - if (isset($fieldElement->options)) { - $columnAnnotation->options = $this->parseOptions($fieldElement->options->children()); - } - - return $columnAnnotation; - } - - private function convertGeneratorElementToGeneratedValueAnnotation( - SimpleXMLElement $generatorElement - ) : Annotation\GeneratedValue { - $generatedValueAnnotation = new Annotation\GeneratedValue(); - - $generatedValueAnnotation->strategy = (string) ($generatorElement['strategy'] ?? 'AUTO'); - - return $generatedValueAnnotation; - } - - private function convertSequenceGeneratorElementToSequenceGeneratorAnnotation( - SimpleXMLElement $sequenceGeneratorElement - ) : Annotation\SequenceGenerator { - $sequenceGeneratorAnnotation = new Annotation\SequenceGenerator(); - - $sequenceGeneratorAnnotation->sequenceName = (string) ($sequenceGeneratorElement['sequence-name'] ?? null); - $sequenceGeneratorAnnotation->allocationSize = (int) ($sequenceGeneratorElement['allocation-size'] ?? 1); - - return $sequenceGeneratorAnnotation; - } - - private function convertCustomIdGeneratorElementToCustomIdGeneratorAnnotation( - SimpleXMLElement $customIdGeneratorElement - ) : Annotation\CustomIdGenerator { - $customIdGeneratorAnnotation = new Annotation\CustomIdGenerator(); - - $customIdGeneratorAnnotation->class = (string) $customIdGeneratorElement['class']; - $customIdGeneratorAnnotation->arguments = []; - - return $customIdGeneratorAnnotation; - } - /** * Constructs a JoinTable annotation based on the information * found in the given SimpleXMLElement. @@ -977,43 +898,6 @@ private function convertJoinColumnElementToJoinColumnAnnotation( return $joinColumnAnnotation; } - /** - * Parse the given Cache as CacheMetadata - */ - private function convertCacheElementToCacheAnnotation(SimpleXMLElement $cacheMapping) : Annotation\Cache - { - $cacheAnnotation = new Annotation\Cache(); - - if (isset($cacheMapping['region'])) { - $cacheAnnotation->region = (string) $cacheMapping['region']; - } - - if (isset($cacheMapping['usage'])) { - $cacheAnnotation->usage = strtoupper((string) $cacheMapping['usage']); - } - - return $cacheAnnotation; - } - - private function convertDiscrimininatorColumnElementToDiscriminatorColumnAnnotation( - SimpleXMLElement $discriminatorColumnElement - ) : Annotation\DiscriminatorColumn { - $discriminatorColumnAnnotation = new Annotation\DiscriminatorColumn(); - - $discriminatorColumnAnnotation->type = (string) ($discriminatorColumnElement['type'] ?? 'string'); - $discriminatorColumnAnnotation->name = (string) $discriminatorColumnElement['name']; - - if (isset($discriminatorColumnElement['column-definition'])) { - $discriminatorColumnAnnotation->columnDefinition = (string) $discriminatorColumnElement['column-definition']; - } - - if (isset($discriminatorColumnElement['length'])) { - $discriminatorColumnAnnotation->length = (int) $discriminatorColumnElement['length']; - } - - return $discriminatorColumnAnnotation; - } - private function convertOrderByElementToOrderByAnnotation( SimpleXMLElement $orderByElement ) : Annotation\OrderBy { @@ -1031,6 +915,38 @@ private function convertOrderByElementToOrderByAnnotation( return $orderByAnnotation; } + private function convertGeneratorElementToGeneratedValueAnnotation( + SimpleXMLElement $generatorElement + ) : Annotation\GeneratedValue { + $generatedValueAnnotation = new Annotation\GeneratedValue(); + + $generatedValueAnnotation->strategy = (string) ($generatorElement['strategy'] ?? 'AUTO'); + + return $generatedValueAnnotation; + } + + private function convertSequenceGeneratorElementToSequenceGeneratorAnnotation( + SimpleXMLElement $sequenceGeneratorElement + ) : Annotation\SequenceGenerator { + $sequenceGeneratorAnnotation = new Annotation\SequenceGenerator(); + + $sequenceGeneratorAnnotation->sequenceName = (string) ($sequenceGeneratorElement['sequence-name'] ?? null); + $sequenceGeneratorAnnotation->allocationSize = (int) ($sequenceGeneratorElement['allocation-size'] ?? 1); + + return $sequenceGeneratorAnnotation; + } + + private function convertCustomIdGeneratorElementToCustomIdGeneratorAnnotation( + SimpleXMLElement $customIdGeneratorElement + ) : Annotation\CustomIdGenerator { + $customIdGeneratorAnnotation = new Annotation\CustomIdGenerator(); + + $customIdGeneratorAnnotation->class = (string) $customIdGeneratorElement['class']; + $customIdGeneratorAnnotation->arguments = []; + + return $customIdGeneratorAnnotation; + } + /** * Gathers a list of cascade options found in the given cascade element. * @@ -1056,43 +972,49 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement) : array } /** - * {@inheritDoc} + * @param mixed $element + * + * @return bool */ - protected function loadMappingFile($file) + private function evaluateBoolean($element) { - $result = []; - // Note: we do not use `simplexml_load_file()` because of https://bugs.php.net/bug.php?id=62577 - $xmlElement = simplexml_load_string(file_get_contents($file)); - - if (isset($xmlElement->entity)) { - foreach ($xmlElement->entity as $entityElement) { - $entityName = (string) $entityElement['name']; - $result[$entityName] = $entityElement; - } - } elseif (isset($xmlElement->{'mapped-superclass'})) { - foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { - $className = (string) $mappedSuperClass['name']; - $result[$className] = $mappedSuperClass; - } - } elseif (isset($xmlElement->embeddable)) { - foreach ($xmlElement->embeddable as $embeddableElement) { - $embeddableName = (string) $embeddableElement['name']; - $result[$embeddableName] = $embeddableElement; - } - } + $flag = (string) $element; - return $result; + return $flag === 'true' || $flag === '1'; } /** - * @param mixed $element + * Parses (nested) option elements. * - * @return bool + * @param SimpleXMLElement $options The XML element. + * + * @return mixed[] The options array. */ - protected function evaluateBoolean($element) + private function parseOptions(SimpleXMLElement $options) : array { - $flag = (string) $element; + $array = []; - return $flag === 'true' || $flag === '1'; + /** @var SimpleXMLElement $option */ + foreach ($options as $option) { + if ($option->count()) { + $value = $this->parseOptions($option->children()); + } else { + $value = (string) $option; + } + + $attributes = $option->attributes(); + + if (isset($attributes->name)) { + $nameAttribute = (string) $attributes->name; + + $array[$nameAttribute] = in_array($nameAttribute, ['unsigned', 'fixed'], true) + ? $this->evaluateBoolean($value) + : $value; + } else { + $array[] = $value; + } + } + + return $array; } }