diff --git a/Form/DoctrineOrmTypeGuesser.php b/Form/DoctrineOrmTypeGuesser.php index 19b683b3..6ef7b258 100644 --- a/Form/DoctrineOrmTypeGuesser.php +++ b/Form/DoctrineOrmTypeGuesser.php @@ -14,6 +14,7 @@ use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\JoinColumnMapping; use Doctrine\ORM\Mapping\MappingException as LegacyMappingException; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\Mapping\MappingException; @@ -110,13 +111,13 @@ public function guessRequired(string $class, string $property): ?ValueGuess if ($classMetadata->isAssociationWithSingleJoinColumn($property)) { $mapping = $classMetadata->getAssociationMapping($property); - if (!isset($mapping['joinColumns'][0]['nullable'])) { + if (null === self::getMappingValue($mapping['joinColumns'][0], 'nullable')) { // The "nullable" option defaults to true, in that case the // field should not be required. return new ValueGuess(false, Guess::HIGH_CONFIDENCE); } - return new ValueGuess(!$mapping['joinColumns'][0]['nullable'], Guess::HIGH_CONFIDENCE); + return new ValueGuess(!self::getMappingValue($mapping['joinColumns'][0], 'nullable'), Guess::HIGH_CONFIDENCE); } return null; @@ -190,4 +191,13 @@ private static function getRealClass(string $class): string return substr($class, $pos + Proxy::MARKER_LENGTH + 2); } + + private static function getMappingValue(array|JoinColumnMapping $mapping, string $key): mixed + { + if ($mapping instanceof JoinColumnMapping) { + return $mapping->$key; + } + + return $mapping[$key] ?? null; + } } diff --git a/PropertyInfo/DoctrineExtractor.php b/PropertyInfo/DoctrineExtractor.php index 89edf1ce..a4b23034 100644 --- a/PropertyInfo/DoctrineExtractor.php +++ b/PropertyInfo/DoctrineExtractor.php @@ -16,6 +16,9 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\EmbeddedClassMapping; +use Doctrine\ORM\Mapping\FieldMapping; +use Doctrine\ORM\Mapping\JoinColumnMapping; use Doctrine\ORM\Mapping\MappingException as OrmMappingException; use Doctrine\Persistence\Mapping\MappingException; use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; @@ -78,20 +81,20 @@ public function getTypes(string $class, string $property, array $context = []): if ($metadata instanceof ClassMetadata) { $associationMapping = $metadata->getAssociationMapping($property); - if (isset($associationMapping['indexBy'])) { - $subMetadata = $this->entityManager->getClassMetadata($associationMapping['targetEntity']); + if (self::getMappingValue($associationMapping, 'indexBy')) { + $subMetadata = $this->entityManager->getClassMetadata(self::getMappingValue($associationMapping, 'targetEntity')); // Check if indexBy value is a property - $fieldName = $associationMapping['indexBy']; + $fieldName = self::getMappingValue($associationMapping, 'indexBy'); if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) { - $fieldName = $subMetadata->getFieldForColumn($associationMapping['indexBy']); + $fieldName = $subMetadata->getFieldForColumn(self::getMappingValue($associationMapping, 'indexBy')); // Not a property, maybe a column name? if (null === ($typeOfField = $subMetadata->getTypeOfField($fieldName))) { // Maybe the column name is the association join column? $associationMapping = $subMetadata->getAssociationMapping($fieldName); $indexProperty = $subMetadata->getSingleAssociationReferencedJoinColumnName($fieldName); - $subMetadata = $this->entityManager->getClassMetadata($associationMapping['targetEntity']); + $subMetadata = $this->entityManager->getClassMetadata(self::getMappingValue($associationMapping, 'targetEntity')); // Not a property, maybe a column name? if (null === ($typeOfField = $subMetadata->getTypeOfField($indexProperty))) { @@ -118,7 +121,7 @@ public function getTypes(string $class, string $property, array $context = []): } if ($metadata instanceof ClassMetadata && isset($metadata->embeddedClasses[$property])) { - return [new Type(Type::BUILTIN_TYPE_OBJECT, false, $metadata->embeddedClasses[$property]['class'])]; + return [new Type(Type::BUILTIN_TYPE_OBJECT, false, self::getMappingValue($metadata->embeddedClasses[$property], 'class'))]; } if ($metadata->hasField($property)) { @@ -130,7 +133,7 @@ public function getTypes(string $class, string $property, array $context = []): $nullable = $metadata instanceof ClassMetadata && $metadata->isNullable($property); $enumType = null; - if (null !== $enumClass = $metadata->getFieldMapping($property)['enumType'] ?? null) { + if (null !== $enumClass = self::getMappingValue($metadata->getFieldMapping($property), 'enumType') ?? null) { $enumType = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $enumClass); } @@ -220,17 +223,17 @@ private function getMetadata(string $class): ?ClassMetadata */ private function isAssociationNullable(array|AssociationMapping $associationMapping): bool { - if (isset($associationMapping['id']) && $associationMapping['id']) { + if (self::getMappingValue($associationMapping, 'id')) { return false; } - if (!isset($associationMapping['joinColumns'])) { + if (!self::getMappingValue($associationMapping, 'joinColumns')) { return true; } - $joinColumns = $associationMapping['joinColumns']; + $joinColumns = self::getMappingValue($associationMapping, 'joinColumns'); foreach ($joinColumns as $joinColumn) { - if (isset($joinColumn['nullable']) && !$joinColumn['nullable']) { + if (false === self::getMappingValue($joinColumn, 'nullable')) { return false; } } @@ -272,4 +275,13 @@ private function getPhpType(string $doctrineType): ?string default => null, }; } + + private static function getMappingValue(array|AssociationMapping|EmbeddedClassMapping|FieldMapping|JoinColumnMapping $mapping, string $key): mixed + { + if ($mapping instanceof AssociationMapping || $mapping instanceof EmbeddedClassMapping || $mapping instanceof FieldMapping || $mapping instanceof JoinColumnMapping) { + return $mapping->$key; + } + + return $mapping[$key] ?? null; + } } diff --git a/Validator/DoctrineLoader.php b/Validator/DoctrineLoader.php index e3a93995..50aa917c 100644 --- a/Validator/DoctrineLoader.php +++ b/Validator/DoctrineLoader.php @@ -13,6 +13,7 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata as OrmClassMetadata; +use Doctrine\ORM\Mapping\FieldMapping; use Doctrine\ORM\Mapping\MappingException as OrmMappingException; use Doctrine\Persistence\Mapping\MappingException; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; @@ -69,7 +70,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool foreach ($doctrineMetadata->fieldMappings as $mapping) { $enabledForProperty = $enabledForClass; $lengthConstraint = null; - foreach ($metadata->getPropertyMetadata($mapping['fieldName']) as $propertyMetadata) { + foreach ($metadata->getPropertyMetadata(self::getFieldMappingValue($mapping, 'fieldName')) as $propertyMetadata) { // Enabling or disabling auto-mapping explicitly always takes precedence if (AutoMappingStrategy::DISABLED === $propertyMetadata->getAutoMappingStrategy()) { continue 2; @@ -89,26 +90,26 @@ public function loadClassMetadata(ClassMetadata $metadata): bool continue; } - if (true === ($mapping['unique'] ?? false) && !isset($existingUniqueFields[$mapping['fieldName']])) { - $metadata->addConstraint(new UniqueEntity(['fields' => $mapping['fieldName']])); + if (true === (self::getFieldMappingValue($mapping, 'unique') ?? false) && !isset($existingUniqueFields[self::getFieldMappingValue($mapping, 'fieldName')])) { + $metadata->addConstraint(new UniqueEntity(['fields' => self::getFieldMappingValue($mapping, 'fieldName')])); $loaded = true; } - if (null === ($mapping['length'] ?? null) || null !== ($mapping['enumType'] ?? null) || !\in_array($mapping['type'], ['string', 'text'], true)) { + if (null === (self::getFieldMappingValue($mapping, 'length') ?? null) || null !== (self::getFieldMappingValue($mapping, 'enumType') ?? null) || !\in_array(self::getFieldMappingValue($mapping, 'type'), ['string', 'text'], true)) { continue; } if (null === $lengthConstraint) { - if (isset($mapping['originalClass']) && !str_contains($mapping['declaredField'], '.')) { - $metadata->addPropertyConstraint($mapping['declaredField'], new Valid()); + if (self::getFieldMappingValue($mapping, 'originalClass') && !str_contains(self::getFieldMappingValue($mapping, 'declaredField'), '.')) { + $metadata->addPropertyConstraint(self::getFieldMappingValue($mapping, 'declaredField'), new Valid()); $loaded = true; - } elseif (property_exists($className, $mapping['fieldName']) && (!$doctrineMetadata->isMappedSuperclass || $metadata->getReflectionClass()->getProperty($mapping['fieldName'])->isPrivate())) { - $metadata->addPropertyConstraint($mapping['fieldName'], new Length(['max' => $mapping['length']])); + } elseif (property_exists($className, self::getFieldMappingValue($mapping, 'fieldName')) && (!$doctrineMetadata->isMappedSuperclass || $metadata->getReflectionClass()->getProperty(self::getFieldMappingValue($mapping, 'fieldName'))->isPrivate())) { + $metadata->addPropertyConstraint(self::getFieldMappingValue($mapping, 'fieldName'), new Length(['max' => self::getFieldMappingValue($mapping, 'length')])); $loaded = true; } } elseif (null === $lengthConstraint->max) { // If a Length constraint exists and no max length has been explicitly defined, set it - $lengthConstraint->max = $mapping['length']; + $lengthConstraint->max = self::getFieldMappingValue($mapping, 'length'); } } @@ -132,4 +133,13 @@ private function getExistingUniqueFields(ClassMetadata $metadata): array return $fields; } + + private static function getFieldMappingValue(array|FieldMapping $mapping, string $key): mixed + { + if ($mapping instanceof FieldMapping) { + return $mapping->$key; + } + + return $mapping[$key] ?? null; + } }