diff options
| author | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
| commit | bf6655a534a6775d30cafa67bd801276bda1d98d (patch) | |
| tree | c6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/orm/src/Tools/SchemaValidator.php | |
| parent | 94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff) | |
| download | AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.gz AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.bz2 AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip | |
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/orm/src/Tools/SchemaValidator.php')
| -rw-r--r-- | vendor/doctrine/orm/src/Tools/SchemaValidator.php | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/vendor/doctrine/orm/src/Tools/SchemaValidator.php b/vendor/doctrine/orm/src/Tools/SchemaValidator.php new file mode 100644 index 0000000..fdfc003 --- /dev/null +++ b/vendor/doctrine/orm/src/Tools/SchemaValidator.php | |||
| @@ -0,0 +1,443 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\ORM\Tools; | ||
| 6 | |||
| 7 | use BackedEnum; | ||
| 8 | use Doctrine\DBAL\Types\AsciiStringType; | ||
| 9 | use Doctrine\DBAL\Types\BigIntType; | ||
| 10 | use Doctrine\DBAL\Types\BooleanType; | ||
| 11 | use Doctrine\DBAL\Types\DecimalType; | ||
| 12 | use Doctrine\DBAL\Types\FloatType; | ||
| 13 | use Doctrine\DBAL\Types\GuidType; | ||
| 14 | use Doctrine\DBAL\Types\IntegerType; | ||
| 15 | use Doctrine\DBAL\Types\JsonType; | ||
| 16 | use Doctrine\DBAL\Types\SimpleArrayType; | ||
| 17 | use Doctrine\DBAL\Types\SmallIntType; | ||
| 18 | use Doctrine\DBAL\Types\StringType; | ||
| 19 | use Doctrine\DBAL\Types\TextType; | ||
| 20 | use Doctrine\DBAL\Types\Type; | ||
| 21 | use Doctrine\ORM\EntityManagerInterface; | ||
| 22 | use Doctrine\ORM\Mapping\ClassMetadata; | ||
| 23 | use Doctrine\ORM\Mapping\FieldMapping; | ||
| 24 | use ReflectionEnum; | ||
| 25 | use ReflectionNamedType; | ||
| 26 | |||
| 27 | use function array_diff; | ||
| 28 | use function array_filter; | ||
| 29 | use function array_key_exists; | ||
| 30 | use function array_map; | ||
| 31 | use function array_push; | ||
| 32 | use function array_search; | ||
| 33 | use function array_values; | ||
| 34 | use function assert; | ||
| 35 | use function class_exists; | ||
| 36 | use function class_parents; | ||
| 37 | use function count; | ||
| 38 | use function implode; | ||
| 39 | use function in_array; | ||
| 40 | use function interface_exists; | ||
| 41 | use function is_a; | ||
| 42 | use function sprintf; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Performs strict validation of the mapping schema | ||
| 46 | * | ||
| 47 | * @link www.doctrine-project.com | ||
| 48 | */ | ||
| 49 | class SchemaValidator | ||
| 50 | { | ||
| 51 | /** | ||
| 52 | * It maps built-in Doctrine types to PHP types | ||
| 53 | */ | ||
| 54 | private const BUILTIN_TYPES_MAP = [ | ||
| 55 | AsciiStringType::class => ['string'], | ||
| 56 | BigIntType::class => ['int', 'string'], | ||
| 57 | BooleanType::class => ['bool'], | ||
| 58 | DecimalType::class => ['string'], | ||
| 59 | FloatType::class => ['float'], | ||
| 60 | GuidType::class => ['string'], | ||
| 61 | IntegerType::class => ['int'], | ||
| 62 | JsonType::class => ['array'], | ||
| 63 | SimpleArrayType::class => ['array'], | ||
| 64 | SmallIntType::class => ['int'], | ||
| 65 | StringType::class => ['string'], | ||
| 66 | TextType::class => ['string'], | ||
| 67 | ]; | ||
| 68 | |||
| 69 | public function __construct( | ||
| 70 | private readonly EntityManagerInterface $em, | ||
| 71 | private readonly bool $validatePropertyTypes = true, | ||
| 72 | ) { | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Checks the internal consistency of all mapping files. | ||
| 77 | * | ||
| 78 | * There are several checks that can't be done at runtime or are too expensive, which can be verified | ||
| 79 | * with this command. For example: | ||
| 80 | * | ||
| 81 | * 1. Check if a relation with "mappedBy" is actually connected to that specified field. | ||
| 82 | * 2. Check if "mappedBy" and "inversedBy" are consistent to each other. | ||
| 83 | * 3. Check if "referencedColumnName" attributes are really pointing to primary key columns. | ||
| 84 | * | ||
| 85 | * @psalm-return array<string, list<string>> | ||
| 86 | */ | ||
| 87 | public function validateMapping(): array | ||
| 88 | { | ||
| 89 | $errors = []; | ||
| 90 | $cmf = $this->em->getMetadataFactory(); | ||
| 91 | $classes = $cmf->getAllMetadata(); | ||
| 92 | |||
| 93 | foreach ($classes as $class) { | ||
| 94 | $ce = $this->validateClass($class); | ||
| 95 | if ($ce) { | ||
| 96 | $errors[$class->name] = $ce; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | return $errors; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** | ||
| 104 | * Validates a single class of the current. | ||
| 105 | * | ||
| 106 | * @return string[] | ||
| 107 | * @psalm-return list<string> | ||
| 108 | */ | ||
| 109 | public function validateClass(ClassMetadata $class): array | ||
| 110 | { | ||
| 111 | $ce = []; | ||
| 112 | $cmf = $this->em->getMetadataFactory(); | ||
| 113 | |||
| 114 | foreach ($class->fieldMappings as $fieldName => $mapping) { | ||
| 115 | if (! Type::hasType($mapping->type)) { | ||
| 116 | $ce[] = "The field '" . $class->name . '#' . $fieldName . "' uses a non-existent type '" . $mapping->type . "'."; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | if ($this->validatePropertyTypes) { | ||
| 121 | array_push($ce, ...$this->validatePropertiesTypes($class)); | ||
| 122 | } | ||
| 123 | |||
| 124 | foreach ($class->associationMappings as $fieldName => $assoc) { | ||
| 125 | if (! class_exists($assoc->targetEntity) || $cmf->isTransient($assoc->targetEntity)) { | ||
| 126 | $ce[] = "The target entity '" . $assoc->targetEntity . "' specified on " . $class->name . '#' . $fieldName . ' is unknown or not an entity.'; | ||
| 127 | |||
| 128 | return $ce; | ||
| 129 | } | ||
| 130 | |||
| 131 | $targetMetadata = $cmf->getMetadataFor($assoc->targetEntity); | ||
| 132 | |||
| 133 | if ($targetMetadata->isMappedSuperclass) { | ||
| 134 | $ce[] = "The target entity '" . $assoc->targetEntity . "' specified on " . $class->name . '#' . $fieldName . ' is a mapped superclass. This is not possible since there is no table that a foreign key could refer to.'; | ||
| 135 | |||
| 136 | return $ce; | ||
| 137 | } | ||
| 138 | |||
| 139 | if (isset($assoc->id) && $targetMetadata->containsForeignIdentifier) { | ||
| 140 | $ce[] = "Cannot map association '" . $class->name . '#' . $fieldName . ' as identifier, because ' . | ||
| 141 | "the target entity '" . $targetMetadata->name . "' also maps an association as identifier."; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (! $assoc->isOwningSide()) { | ||
| 145 | if ($targetMetadata->hasField($assoc->mappedBy)) { | ||
| 146 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' . | ||
| 147 | 'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which is not defined as association, but as field.'; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (! $targetMetadata->hasAssociation($assoc->mappedBy)) { | ||
| 151 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the owning side ' . | ||
| 152 | 'field ' . $assoc->targetEntity . '#' . $assoc->mappedBy . ' which does not exist.'; | ||
| 153 | } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy === null) { | ||
| 154 | $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the inverse side of a ' . | ||
| 155 | 'bi-directional relationship, but the specified mappedBy association on the target-entity ' . | ||
| 156 | $assoc->targetEntity . '#' . $assoc->mappedBy . ' does not contain the required ' . | ||
| 157 | "'inversedBy=\"" . $fieldName . "\"' attribute."; | ||
| 158 | } elseif ($targetMetadata->associationMappings[$assoc->mappedBy]->inversedBy !== $fieldName) { | ||
| 159 | $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' . | ||
| 160 | $assoc->targetEntity . '#' . $assoc->mappedBy . ' are ' . | ||
| 161 | 'inconsistent with each other.'; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | if ($assoc->isOwningSide() && $assoc->inversedBy !== null) { | ||
| 166 | if ($targetMetadata->hasField($assoc->inversedBy)) { | ||
| 167 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' . | ||
| 168 | 'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which is not defined as association.'; | ||
| 169 | } | ||
| 170 | |||
| 171 | if (! $targetMetadata->hasAssociation($assoc->inversedBy)) { | ||
| 172 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' refers to the inverse side ' . | ||
| 173 | 'field ' . $assoc->targetEntity . '#' . $assoc->inversedBy . ' which does not exist.'; | ||
| 174 | } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->isOwningSide()) { | ||
| 175 | $ce[] = 'The field ' . $class->name . '#' . $fieldName . ' is on the owning side of a ' . | ||
| 176 | 'bi-directional relationship, but the specified inversedBy association on the target-entity ' . | ||
| 177 | $assoc->targetEntity . '#' . $assoc->inversedBy . ' does not contain the required ' . | ||
| 178 | "'mappedBy=\"" . $fieldName . "\"' attribute."; | ||
| 179 | } elseif ($targetMetadata->associationMappings[$assoc->inversedBy]->mappedBy !== $fieldName) { | ||
| 180 | $ce[] = 'The mappings ' . $class->name . '#' . $fieldName . ' and ' . | ||
| 181 | $assoc->targetEntity . '#' . $assoc->inversedBy . ' are ' . | ||
| 182 | 'inconsistent with each other.'; | ||
| 183 | } | ||
| 184 | |||
| 185 | // Verify inverse side/owning side match each other | ||
| 186 | if (array_key_exists($assoc->inversedBy, $targetMetadata->associationMappings)) { | ||
| 187 | $targetAssoc = $targetMetadata->associationMappings[$assoc->inversedBy]; | ||
| 188 | if ($assoc->isOneToOne() && ! $targetAssoc->isOneToOne()) { | ||
| 189 | $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is one-to-one, then the inversed ' . | ||
| 190 | 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-one as well.'; | ||
| 191 | } elseif ($assoc->isManyToOne() && ! $targetAssoc->isOneToMany()) { | ||
| 192 | $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-one, then the inversed ' . | ||
| 193 | 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be one-to-many.'; | ||
| 194 | } elseif ($assoc->isManyToMany() && ! $targetAssoc->isManyToMany()) { | ||
| 195 | $ce[] = 'If association ' . $class->name . '#' . $fieldName . ' is many-to-many, then the inversed ' . | ||
| 196 | 'side ' . $targetMetadata->name . '#' . $assoc->inversedBy . ' has to be many-to-many as well.'; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | |||
| 201 | if ($assoc->isOwningSide()) { | ||
| 202 | if ($assoc->isManyToManyOwningSide()) { | ||
| 203 | $identifierColumns = $class->getIdentifierColumnNames(); | ||
| 204 | foreach ($assoc->joinTable->joinColumns as $joinColumn) { | ||
| 205 | if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) { | ||
| 206 | $ce[] = "The referenced column name '" . $joinColumn->referencedColumnName . "' " . | ||
| 207 | "has to be a primary key column on the target entity class '" . $class->name . "'."; | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | $identifierColumns = $targetMetadata->getIdentifierColumnNames(); | ||
| 213 | foreach ($assoc->joinTable->inverseJoinColumns as $inverseJoinColumn) { | ||
| 214 | if (! in_array($inverseJoinColumn->referencedColumnName, $identifierColumns, true)) { | ||
| 215 | $ce[] = "The referenced column name '" . $inverseJoinColumn->referencedColumnName . "' " . | ||
| 216 | "has to be a primary key column on the target entity class '" . $targetMetadata->name . "'."; | ||
| 217 | break; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | if (count($targetMetadata->getIdentifierColumnNames()) !== count($assoc->joinTable->inverseJoinColumns)) { | ||
| 222 | $ce[] = "The inverse join columns of the many-to-many table '" . $assoc->joinTable->name . "' " . | ||
| 223 | "have to contain to ALL identifier columns of the target entity '" . $targetMetadata->name . "', " . | ||
| 224 | "however '" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), array_values($assoc->relationToTargetKeyColumns))) . | ||
| 225 | "' are missing."; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (count($class->getIdentifierColumnNames()) !== count($assoc->joinTable->joinColumns)) { | ||
| 229 | $ce[] = "The join columns of the many-to-many table '" . $assoc->joinTable->name . "' " . | ||
| 230 | "have to contain to ALL identifier columns of the source entity '" . $class->name . "', " . | ||
| 231 | "however '" . implode(', ', array_diff($class->getIdentifierColumnNames(), array_values($assoc->relationToSourceKeyColumns))) . | ||
| 232 | "' are missing."; | ||
| 233 | } | ||
| 234 | } elseif ($assoc->isToOneOwningSide()) { | ||
| 235 | $identifierColumns = $targetMetadata->getIdentifierColumnNames(); | ||
| 236 | foreach ($assoc->joinColumns as $joinColumn) { | ||
| 237 | if (! in_array($joinColumn->referencedColumnName, $identifierColumns, true)) { | ||
| 238 | $ce[] = "The referenced column name '" . $joinColumn->referencedColumnName . "' " . | ||
| 239 | "has to be a primary key column on the target entity class '" . $targetMetadata->name . "'."; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | if (count($identifierColumns) !== count($assoc->joinColumns)) { | ||
| 244 | $ids = []; | ||
| 245 | |||
| 246 | foreach ($assoc->joinColumns as $joinColumn) { | ||
| 247 | $ids[] = $joinColumn->name; | ||
| 248 | } | ||
| 249 | |||
| 250 | $ce[] = "The join columns of the association '" . $assoc->fieldName . "' " . | ||
| 251 | "have to match to ALL identifier columns of the target entity '" . $targetMetadata->name . "', " . | ||
| 252 | "however '" . implode(', ', array_diff($targetMetadata->getIdentifierColumnNames(), $ids)) . | ||
| 253 | "' are missing."; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | if ($assoc->isOrdered()) { | ||
| 259 | foreach ($assoc->orderBy() as $orderField => $orientation) { | ||
| 260 | if (! $targetMetadata->hasField($orderField) && ! $targetMetadata->hasAssociation($orderField)) { | ||
| 261 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a foreign field ' . | ||
| 262 | $orderField . ' that is not a field on the target entity ' . $targetMetadata->name . '.'; | ||
| 263 | continue; | ||
| 264 | } | ||
| 265 | |||
| 266 | if ($targetMetadata->isCollectionValuedAssociation($orderField)) { | ||
| 267 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a field ' . | ||
| 268 | $orderField . ' on ' . $targetMetadata->name . ' that is a collection-valued association.'; | ||
| 269 | continue; | ||
| 270 | } | ||
| 271 | |||
| 272 | if ($targetMetadata->isAssociationInverseSide($orderField)) { | ||
| 273 | $ce[] = 'The association ' . $class->name . '#' . $fieldName . ' is ordered by a field ' . | ||
| 274 | $orderField . ' on ' . $targetMetadata->name . ' that is the inverse side of an association.'; | ||
| 275 | continue; | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | } | ||
| 280 | |||
| 281 | if ( | ||
| 282 | ! $class->isInheritanceTypeNone() | ||
| 283 | && ! $class->isRootEntity() | ||
| 284 | && ($class->reflClass !== null && ! $class->reflClass->isAbstract()) | ||
| 285 | && ! $class->isMappedSuperclass | ||
| 286 | && array_search($class->name, $class->discriminatorMap, true) === false | ||
| 287 | ) { | ||
| 288 | $ce[] = "Entity class '" . $class->name . "' is part of inheritance hierarchy, but is " . | ||
| 289 | "not mapped in the root entity '" . $class->rootEntityName . "' discriminator map. " . | ||
| 290 | 'All subclasses must be listed in the discriminator map.'; | ||
| 291 | } | ||
| 292 | |||
| 293 | foreach ($class->subClasses as $subClass) { | ||
| 294 | if (! in_array($class->name, class_parents($subClass), true)) { | ||
| 295 | $ce[] = "According to the discriminator map class '" . $subClass . "' has to be a child " . | ||
| 296 | "of '" . $class->name . "' but these entities are not related through inheritance."; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | return $ce; | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 304 | * Checks if the Database Schema is in sync with the current metadata state. | ||
| 305 | */ | ||
| 306 | public function schemaInSyncWithMetadata(): bool | ||
| 307 | { | ||
| 308 | return count($this->getUpdateSchemaList()) === 0; | ||
| 309 | } | ||
| 310 | |||
| 311 | /** | ||
| 312 | * Returns the list of missing Database Schema updates. | ||
| 313 | * | ||
| 314 | * @return array<string> | ||
| 315 | */ | ||
| 316 | public function getUpdateSchemaList(): array | ||
| 317 | { | ||
| 318 | $schemaTool = new SchemaTool($this->em); | ||
| 319 | |||
| 320 | $allMetadata = $this->em->getMetadataFactory()->getAllMetadata(); | ||
| 321 | |||
| 322 | return $schemaTool->getUpdateSchemaSql($allMetadata); | ||
| 323 | } | ||
| 324 | |||
| 325 | /** @return list<string> containing the found issues */ | ||
| 326 | private function validatePropertiesTypes(ClassMetadata $class): array | ||
| 327 | { | ||
| 328 | return array_values( | ||
| 329 | array_filter( | ||
| 330 | array_map( | ||
| 331 | function (FieldMapping $fieldMapping) use ($class): string|null { | ||
| 332 | $fieldName = $fieldMapping->fieldName; | ||
| 333 | assert(isset($class->reflFields[$fieldName])); | ||
| 334 | $propertyType = $class->reflFields[$fieldName]->getType(); | ||
| 335 | |||
| 336 | // If the field type is not a built-in type, we cannot check it | ||
| 337 | if (! Type::hasType($fieldMapping->type)) { | ||
| 338 | return null; | ||
| 339 | } | ||
| 340 | |||
| 341 | // If the property type is not a named type, we cannot check it | ||
| 342 | if (! ($propertyType instanceof ReflectionNamedType) || $propertyType->getName() === 'mixed') { | ||
| 343 | return null; | ||
| 344 | } | ||
| 345 | |||
| 346 | $metadataFieldType = $this->findBuiltInType(Type::getType($fieldMapping->type)); | ||
| 347 | |||
| 348 | //If the metadata field type is not a mapped built-in type, we cannot check it | ||
| 349 | if ($metadataFieldType === null) { | ||
| 350 | return null; | ||
| 351 | } | ||
| 352 | |||
| 353 | $propertyType = $propertyType->getName(); | ||
| 354 | |||
| 355 | // If the property type is the same as the metadata field type, we are ok | ||
| 356 | if (in_array($propertyType, $metadataFieldType, true)) { | ||
| 357 | return null; | ||
| 358 | } | ||
| 359 | |||
| 360 | if (is_a($propertyType, BackedEnum::class, true)) { | ||
| 361 | $backingType = (string) (new ReflectionEnum($propertyType))->getBackingType(); | ||
| 362 | |||
| 363 | if (! in_array($backingType, $metadataFieldType, true)) { | ||
| 364 | return sprintf( | ||
| 365 | "The field '%s#%s' has the property type '%s' with a backing type of '%s' that differs from the metadata field type '%s'.", | ||
| 366 | $class->name, | ||
| 367 | $fieldName, | ||
| 368 | $propertyType, | ||
| 369 | $backingType, | ||
| 370 | implode('|', $metadataFieldType), | ||
| 371 | ); | ||
| 372 | } | ||
| 373 | |||
| 374 | if (! isset($fieldMapping->enumType) || $propertyType === $fieldMapping->enumType) { | ||
| 375 | return null; | ||
| 376 | } | ||
| 377 | |||
| 378 | return sprintf( | ||
| 379 | "The field '%s#%s' has the property type '%s' that differs from the metadata enumType '%s'.", | ||
| 380 | $class->name, | ||
| 381 | $fieldName, | ||
| 382 | $propertyType, | ||
| 383 | $fieldMapping->enumType, | ||
| 384 | ); | ||
| 385 | } | ||
| 386 | |||
| 387 | if ( | ||
| 388 | isset($fieldMapping->enumType) | ||
| 389 | && $propertyType !== $fieldMapping->enumType | ||
| 390 | && interface_exists($propertyType) | ||
| 391 | && is_a($fieldMapping->enumType, $propertyType, true) | ||
| 392 | ) { | ||
| 393 | $backingType = (string) (new ReflectionEnum($fieldMapping->enumType))->getBackingType(); | ||
| 394 | |||
| 395 | if (in_array($backingType, $metadataFieldType, true)) { | ||
| 396 | return null; | ||
| 397 | } | ||
| 398 | |||
| 399 | return sprintf( | ||
| 400 | "The field '%s#%s' has the metadata enumType '%s' with a backing type of '%s' that differs from the metadata field type '%s'.", | ||
| 401 | $class->name, | ||
| 402 | $fieldName, | ||
| 403 | $fieldMapping->enumType, | ||
| 404 | $backingType, | ||
| 405 | implode('|', $metadataFieldType), | ||
| 406 | ); | ||
| 407 | } | ||
| 408 | |||
| 409 | if ( | ||
| 410 | $fieldMapping->type === 'json' | ||
| 411 | && in_array($propertyType, ['string', 'int', 'float', 'bool', 'true', 'false', 'null'], true) | ||
| 412 | ) { | ||
| 413 | return null; | ||
| 414 | } | ||
| 415 | |||
| 416 | return sprintf( | ||
| 417 | "The field '%s#%s' has the property type '%s' that differs from the metadata field type '%s' returned by the '%s' DBAL type.", | ||
| 418 | $class->name, | ||
| 419 | $fieldName, | ||
| 420 | $propertyType, | ||
| 421 | implode('|', $metadataFieldType), | ||
| 422 | $fieldMapping->type, | ||
| 423 | ); | ||
| 424 | }, | ||
| 425 | $class->fieldMappings, | ||
| 426 | ), | ||
| 427 | ), | ||
| 428 | ); | ||
| 429 | } | ||
| 430 | |||
| 431 | /** | ||
| 432 | * The exact DBAL type must be used (no subclasses), since consumers of doctrine/orm may have their own | ||
| 433 | * customization around field types. | ||
| 434 | * | ||
| 435 | * @return list<string>|null | ||
| 436 | */ | ||
| 437 | private function findBuiltInType(Type $type): array|null | ||
| 438 | { | ||
| 439 | $typeName = $type::class; | ||
| 440 | |||
| 441 | return self::BUILTIN_TYPES_MAP[$typeName] ?? null; | ||
| 442 | } | ||
| 443 | } | ||
