Builder
9 months ago
Driver
9 months ago
Exception
9 months ago
Reflection
9 months ago
Annotation.php
9 months ago
AnsiQuoteStrategy.php
9 months ago
AssociationOverride.php
9 months ago
AssociationOverrides.php
9 months ago
AttributeOverride.php
9 months ago
AttributeOverrides.php
9 months ago
Cache.php
9 months ago
ChainTypedFieldMapper.php
9 months ago
ChangeTrackingPolicy.php
9 months ago
ClassMetadata.php
9 months ago
ClassMetadataFactory.php
9 months ago
ClassMetadataInfo.php
9 months ago
Column.php
9 months ago
ColumnResult.php
9 months ago
CustomIdGenerator.php
9 months ago
DefaultEntityListenerResolver.php
9 months ago
DefaultNamingStrategy.php
9 months ago
DefaultQuoteStrategy.php
9 months ago
DefaultTypedFieldMapper.php
9 months ago
DiscriminatorColumn.php
9 months ago
DiscriminatorMap.php
9 months ago
Embeddable.php
9 months ago
Embedded.php
9 months ago
Entity.php
9 months ago
EntityListenerResolver.php
9 months ago
EntityListeners.php
9 months ago
EntityResult.php
9 months ago
FieldResult.php
9 months ago
GeneratedValue.php
9 months ago
HasLifecycleCallbacks.php
9 months ago
Id.php
9 months ago
Index.php
9 months ago
InheritanceType.php
9 months ago
InverseJoinColumn.php
9 months ago
JoinColumn.php
9 months ago
JoinColumnProperties.php
9 months ago
JoinColumns.php
9 months ago
JoinTable.php
9 months ago
ManyToMany.php
9 months ago
ManyToOne.php
9 months ago
MappedSuperclass.php
9 months ago
MappingAttribute.php
9 months ago
MappingException.php
9 months ago
NamedNativeQueries.php
9 months ago
NamedNativeQuery.php
9 months ago
NamedQueries.php
9 months ago
NamedQuery.php
9 months ago
NamingStrategy.php
9 months ago
OneToMany.php
9 months ago
OneToOne.php
9 months ago
OrderBy.php
9 months ago
PostLoad.php
9 months ago
PostPersist.php
9 months ago
PostRemove.php
9 months ago
PostUpdate.php
9 months ago
PreFlush.php
9 months ago
PrePersist.php
9 months ago
PreRemove.php
9 months ago
PreUpdate.php
9 months ago
QuoteStrategy.php
9 months ago
ReflectionEmbeddedProperty.php
9 months ago
ReflectionEnumProperty.php
9 months ago
ReflectionReadonlyProperty.php
9 months ago
SequenceGenerator.php
9 months ago
SqlResultSetMapping.php
9 months ago
SqlResultSetMappings.php
9 months ago
Table.php
9 months ago
TypedFieldMapper.php
9 months ago
UnderscoreNamingStrategy.php
9 months ago
UniqueConstraint.php
9 months ago
Version.php
9 months ago
ClassMetadataInfo.php
1588 lines
| 1 | <?php |
| 2 | declare (strict_types=1); |
| 3 | namespace MailPoetVendor\Doctrine\ORM\Mapping; |
| 4 | if (!defined('ABSPATH')) exit; |
| 5 | use BackedEnum; |
| 6 | use BadMethodCallException; |
| 7 | use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform; |
| 8 | use MailPoetVendor\Doctrine\DBAL\Types\Type; |
| 9 | use MailPoetVendor\Doctrine\Deprecations\Deprecation; |
| 10 | use MailPoetVendor\Doctrine\Instantiator\Instantiator; |
| 11 | use MailPoetVendor\Doctrine\Instantiator\InstantiatorInterface; |
| 12 | use MailPoetVendor\Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation; |
| 13 | use MailPoetVendor\Doctrine\ORM\EntityRepository; |
| 14 | use MailPoetVendor\Doctrine\ORM\Id\AbstractIdGenerator; |
| 15 | use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadata; |
| 16 | use MailPoetVendor\Doctrine\Persistence\Mapping\ReflectionService; |
| 17 | use InvalidArgumentException; |
| 18 | use LogicException; |
| 19 | use ReflectionClass; |
| 20 | use ReflectionNamedType; |
| 21 | use ReflectionProperty; |
| 22 | use RuntimeException; |
| 23 | use function array_diff; |
| 24 | use function array_flip; |
| 25 | use function array_intersect; |
| 26 | use function array_keys; |
| 27 | use function array_map; |
| 28 | use function array_merge; |
| 29 | use function array_pop; |
| 30 | use function array_values; |
| 31 | use function assert; |
| 32 | use function class_exists; |
| 33 | use function count; |
| 34 | use function enum_exists; |
| 35 | use function explode; |
| 36 | use function gettype; |
| 37 | use function in_array; |
| 38 | use function interface_exists; |
| 39 | use function is_array; |
| 40 | use function is_subclass_of; |
| 41 | use function ltrim; |
| 42 | use function method_exists; |
| 43 | use function spl_object_id; |
| 44 | use function str_contains; |
| 45 | use function str_replace; |
| 46 | use function strtolower; |
| 47 | use function trait_exists; |
| 48 | use function trim; |
| 49 | use const PHP_VERSION_ID; |
| 50 | class ClassMetadataInfo implements ClassMetadata |
| 51 | { |
| 52 | public const INHERITANCE_TYPE_NONE = 1; |
| 53 | public const INHERITANCE_TYPE_JOINED = 2; |
| 54 | public const INHERITANCE_TYPE_SINGLE_TABLE = 3; |
| 55 | public const INHERITANCE_TYPE_TABLE_PER_CLASS = 4; |
| 56 | public const GENERATOR_TYPE_AUTO = 1; |
| 57 | public const GENERATOR_TYPE_SEQUENCE = 2; |
| 58 | public const GENERATOR_TYPE_TABLE = 3; |
| 59 | public const GENERATOR_TYPE_IDENTITY = 4; |
| 60 | public const GENERATOR_TYPE_NONE = 5; |
| 61 | public const GENERATOR_TYPE_UUID = 6; |
| 62 | public const GENERATOR_TYPE_CUSTOM = 7; |
| 63 | public const CHANGETRACKING_DEFERRED_IMPLICIT = 1; |
| 64 | public const CHANGETRACKING_DEFERRED_EXPLICIT = 2; |
| 65 | public const CHANGETRACKING_NOTIFY = 3; |
| 66 | public const FETCH_LAZY = 2; |
| 67 | public const FETCH_EAGER = 3; |
| 68 | public const FETCH_EXTRA_LAZY = 4; |
| 69 | public const ONE_TO_ONE = 1; |
| 70 | public const MANY_TO_ONE = 2; |
| 71 | public const ONE_TO_MANY = 4; |
| 72 | public const MANY_TO_MANY = 8; |
| 73 | public const TO_ONE = 3; |
| 74 | public const TO_MANY = 12; |
| 75 | public const CACHE_USAGE_READ_ONLY = 1; |
| 76 | public const CACHE_USAGE_NONSTRICT_READ_WRITE = 2; |
| 77 | public const CACHE_USAGE_READ_WRITE = 3; |
| 78 | public const GENERATED_NEVER = 0; |
| 79 | public const GENERATED_INSERT = 1; |
| 80 | public const GENERATED_ALWAYS = 2; |
| 81 | public $name; |
| 82 | public $namespace; |
| 83 | public $rootEntityName; |
| 84 | public $customGeneratorDefinition; |
| 85 | public $customRepositoryClassName; |
| 86 | public $isMappedSuperclass = \false; |
| 87 | public $isEmbeddedClass = \false; |
| 88 | public $parentClasses = []; |
| 89 | public $subClasses = []; |
| 90 | public $embeddedClasses = []; |
| 91 | public $namedQueries = []; |
| 92 | public $namedNativeQueries = []; |
| 93 | public $sqlResultSetMappings = []; |
| 94 | public $identifier = []; |
| 95 | public $inheritanceType = self::INHERITANCE_TYPE_NONE; |
| 96 | public $generatorType = self::GENERATOR_TYPE_NONE; |
| 97 | public $fieldMappings = []; |
| 98 | public $fieldNames = []; |
| 99 | public $columnNames = []; |
| 100 | public $discriminatorValue; |
| 101 | public $discriminatorMap = []; |
| 102 | public $discriminatorColumn; |
| 103 | public $table; |
| 104 | public $lifecycleCallbacks = []; |
| 105 | public $entityListeners = []; |
| 106 | public $associationMappings = []; |
| 107 | public $isIdentifierComposite = \false; |
| 108 | public $containsForeignIdentifier = \false; |
| 109 | public $containsEnumIdentifier = \false; |
| 110 | public $idGenerator; |
| 111 | public $sequenceGeneratorDefinition; |
| 112 | public $tableGeneratorDefinition; |
| 113 | public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT; |
| 114 | public $requiresFetchAfterChange = \false; |
| 115 | public $isVersioned = \false; |
| 116 | public $versionField; |
| 117 | public $cache; |
| 118 | public $reflClass; |
| 119 | public $isReadOnly = \false; |
| 120 | protected $namingStrategy; |
| 121 | public $reflFields = []; |
| 122 | private $instantiator; |
| 123 | private $typedFieldMapper; |
| 124 | public function __construct($entityName, ?NamingStrategy $namingStrategy = null, ?TypedFieldMapper $typedFieldMapper = null) |
| 125 | { |
| 126 | $this->name = $entityName; |
| 127 | $this->rootEntityName = $entityName; |
| 128 | $this->namingStrategy = $namingStrategy ?? new DefaultNamingStrategy(); |
| 129 | $this->instantiator = new Instantiator(); |
| 130 | $this->typedFieldMapper = $typedFieldMapper ?? new DefaultTypedFieldMapper(); |
| 131 | } |
| 132 | public function getReflectionProperties() |
| 133 | { |
| 134 | return $this->reflFields; |
| 135 | } |
| 136 | public function getReflectionProperty($name) |
| 137 | { |
| 138 | return $this->reflFields[$name]; |
| 139 | } |
| 140 | public function getSingleIdReflectionProperty() |
| 141 | { |
| 142 | if ($this->isIdentifierComposite) { |
| 143 | throw new BadMethodCallException('Class ' . $this->name . ' has a composite identifier.'); |
| 144 | } |
| 145 | return $this->reflFields[$this->identifier[0]]; |
| 146 | } |
| 147 | public function getIdentifierValues($entity) |
| 148 | { |
| 149 | if ($this->isIdentifierComposite) { |
| 150 | $id = []; |
| 151 | foreach ($this->identifier as $idField) { |
| 152 | $value = $this->reflFields[$idField]->getValue($entity); |
| 153 | if ($value !== null) { |
| 154 | $id[$idField] = $value; |
| 155 | } |
| 156 | } |
| 157 | return $id; |
| 158 | } |
| 159 | $id = $this->identifier[0]; |
| 160 | $value = $this->reflFields[$id]->getValue($entity); |
| 161 | if ($value === null) { |
| 162 | return []; |
| 163 | } |
| 164 | return [$id => $value]; |
| 165 | } |
| 166 | public function setIdentifierValues($entity, array $id) |
| 167 | { |
| 168 | foreach ($id as $idField => $idValue) { |
| 169 | $this->reflFields[$idField]->setValue($entity, $idValue); |
| 170 | } |
| 171 | } |
| 172 | public function setFieldValue($entity, $field, $value) |
| 173 | { |
| 174 | $this->reflFields[$field]->setValue($entity, $value); |
| 175 | } |
| 176 | public function getFieldValue($entity, $field) |
| 177 | { |
| 178 | return $this->reflFields[$field]->getValue($entity); |
| 179 | } |
| 180 | public function __toString() |
| 181 | { |
| 182 | return self::class . '@' . spl_object_id($this); |
| 183 | } |
| 184 | public function __sleep() |
| 185 | { |
| 186 | // This metadata is always serialized/cached. |
| 187 | $serialized = [ |
| 188 | 'associationMappings', |
| 189 | 'columnNames', |
| 190 | //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName'] |
| 191 | 'fieldMappings', |
| 192 | 'fieldNames', |
| 193 | 'embeddedClasses', |
| 194 | 'identifier', |
| 195 | 'isIdentifierComposite', |
| 196 | // TODO: REMOVE |
| 197 | 'name', |
| 198 | 'namespace', |
| 199 | // TODO: REMOVE |
| 200 | 'table', |
| 201 | 'rootEntityName', |
| 202 | 'idGenerator', |
| 203 | ]; |
| 204 | // The rest of the metadata is only serialized if necessary. |
| 205 | if ($this->changeTrackingPolicy !== self::CHANGETRACKING_DEFERRED_IMPLICIT) { |
| 206 | $serialized[] = 'changeTrackingPolicy'; |
| 207 | } |
| 208 | if ($this->customRepositoryClassName) { |
| 209 | $serialized[] = 'customRepositoryClassName'; |
| 210 | } |
| 211 | if ($this->inheritanceType !== self::INHERITANCE_TYPE_NONE) { |
| 212 | $serialized[] = 'inheritanceType'; |
| 213 | $serialized[] = 'discriminatorColumn'; |
| 214 | $serialized[] = 'discriminatorValue'; |
| 215 | $serialized[] = 'discriminatorMap'; |
| 216 | $serialized[] = 'parentClasses'; |
| 217 | $serialized[] = 'subClasses'; |
| 218 | } |
| 219 | if ($this->generatorType !== self::GENERATOR_TYPE_NONE) { |
| 220 | $serialized[] = 'generatorType'; |
| 221 | if ($this->generatorType === self::GENERATOR_TYPE_SEQUENCE) { |
| 222 | $serialized[] = 'sequenceGeneratorDefinition'; |
| 223 | } |
| 224 | } |
| 225 | if ($this->isMappedSuperclass) { |
| 226 | $serialized[] = 'isMappedSuperclass'; |
| 227 | } |
| 228 | if ($this->isEmbeddedClass) { |
| 229 | $serialized[] = 'isEmbeddedClass'; |
| 230 | } |
| 231 | if ($this->containsForeignIdentifier) { |
| 232 | $serialized[] = 'containsForeignIdentifier'; |
| 233 | } |
| 234 | if ($this->containsEnumIdentifier) { |
| 235 | $serialized[] = 'containsEnumIdentifier'; |
| 236 | } |
| 237 | if ($this->isVersioned) { |
| 238 | $serialized[] = 'isVersioned'; |
| 239 | $serialized[] = 'versionField'; |
| 240 | } |
| 241 | if ($this->lifecycleCallbacks) { |
| 242 | $serialized[] = 'lifecycleCallbacks'; |
| 243 | } |
| 244 | if ($this->entityListeners) { |
| 245 | $serialized[] = 'entityListeners'; |
| 246 | } |
| 247 | if ($this->namedQueries) { |
| 248 | $serialized[] = 'namedQueries'; |
| 249 | } |
| 250 | if ($this->namedNativeQueries) { |
| 251 | $serialized[] = 'namedNativeQueries'; |
| 252 | } |
| 253 | if ($this->sqlResultSetMappings) { |
| 254 | $serialized[] = 'sqlResultSetMappings'; |
| 255 | } |
| 256 | if ($this->isReadOnly) { |
| 257 | $serialized[] = 'isReadOnly'; |
| 258 | } |
| 259 | if ($this->customGeneratorDefinition) { |
| 260 | $serialized[] = 'customGeneratorDefinition'; |
| 261 | } |
| 262 | if ($this->cache) { |
| 263 | $serialized[] = 'cache'; |
| 264 | } |
| 265 | if ($this->requiresFetchAfterChange) { |
| 266 | $serialized[] = 'requiresFetchAfterChange'; |
| 267 | } |
| 268 | return $serialized; |
| 269 | } |
| 270 | public function newInstance() |
| 271 | { |
| 272 | return $this->instantiator->instantiate($this->name); |
| 273 | } |
| 274 | public function wakeupReflection($reflService) |
| 275 | { |
| 276 | // Restore ReflectionClass and properties |
| 277 | $this->reflClass = $reflService->getClass($this->name); |
| 278 | $this->instantiator = $this->instantiator ?: new Instantiator(); |
| 279 | $parentReflFields = []; |
| 280 | foreach ($this->embeddedClasses as $property => $embeddedClass) { |
| 281 | if (isset($embeddedClass['declaredField'])) { |
| 282 | assert($embeddedClass['originalField'] !== null); |
| 283 | $childProperty = $this->getAccessibleProperty($reflService, $this->embeddedClasses[$embeddedClass['declaredField']]['class'], $embeddedClass['originalField']); |
| 284 | assert($childProperty !== null); |
| 285 | $parentReflFields[$property] = new ReflectionEmbeddedProperty($parentReflFields[$embeddedClass['declaredField']], $childProperty, $this->embeddedClasses[$embeddedClass['declaredField']]['class']); |
| 286 | continue; |
| 287 | } |
| 288 | $fieldRefl = $this->getAccessibleProperty($reflService, $embeddedClass['declared'] ?? $this->name, $property); |
| 289 | $parentReflFields[$property] = $fieldRefl; |
| 290 | $this->reflFields[$property] = $fieldRefl; |
| 291 | } |
| 292 | foreach ($this->fieldMappings as $field => $mapping) { |
| 293 | if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) { |
| 294 | $childProperty = $this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']); |
| 295 | assert($childProperty !== null); |
| 296 | if (isset($mapping['enumType'])) { |
| 297 | $childProperty = new ReflectionEnumProperty($childProperty, $mapping['enumType']); |
| 298 | } |
| 299 | $this->reflFields[$field] = new ReflectionEmbeddedProperty($parentReflFields[$mapping['declaredField']], $childProperty, $mapping['originalClass']); |
| 300 | continue; |
| 301 | } |
| 302 | $this->reflFields[$field] = isset($mapping['declared']) ? $this->getAccessibleProperty($reflService, $mapping['declared'], $field) : $this->getAccessibleProperty($reflService, $this->name, $field); |
| 303 | if (isset($mapping['enumType']) && $this->reflFields[$field] !== null) { |
| 304 | $this->reflFields[$field] = new ReflectionEnumProperty($this->reflFields[$field], $mapping['enumType']); |
| 305 | } |
| 306 | } |
| 307 | foreach ($this->associationMappings as $field => $mapping) { |
| 308 | $this->reflFields[$field] = isset($mapping['declared']) ? $this->getAccessibleProperty($reflService, $mapping['declared'], $field) : $this->getAccessibleProperty($reflService, $this->name, $field); |
| 309 | } |
| 310 | } |
| 311 | public function initializeReflection($reflService) |
| 312 | { |
| 313 | $this->reflClass = $reflService->getClass($this->name); |
| 314 | $this->namespace = $reflService->getClassNamespace($this->name); |
| 315 | if ($this->reflClass) { |
| 316 | $this->name = $this->rootEntityName = $this->reflClass->name; |
| 317 | } |
| 318 | $this->table['name'] = $this->namingStrategy->classToTableName($this->name); |
| 319 | } |
| 320 | public function validateIdentifier() |
| 321 | { |
| 322 | if ($this->isMappedSuperclass || $this->isEmbeddedClass) { |
| 323 | return; |
| 324 | } |
| 325 | // Verify & complete identifier mapping |
| 326 | if (!$this->identifier) { |
| 327 | throw MappingException::identifierRequired($this->name); |
| 328 | } |
| 329 | if ($this->usesIdGenerator() && $this->isIdentifierComposite) { |
| 330 | throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name); |
| 331 | } |
| 332 | } |
| 333 | public function validateAssociations() |
| 334 | { |
| 335 | foreach ($this->associationMappings as $mapping) { |
| 336 | if (!class_exists($mapping['targetEntity']) && !interface_exists($mapping['targetEntity']) && !trait_exists($mapping['targetEntity'])) { |
| 337 | throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']); |
| 338 | } |
| 339 | } |
| 340 | } |
| 341 | public function validateLifecycleCallbacks($reflService) |
| 342 | { |
| 343 | foreach ($this->lifecycleCallbacks as $callbacks) { |
| 344 | foreach ($callbacks as $callbackFuncName) { |
| 345 | if (!$reflService->hasPublicMethod($this->name, $callbackFuncName)) { |
| 346 | throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName); |
| 347 | } |
| 348 | } |
| 349 | } |
| 350 | } |
| 351 | public function getReflectionClass() |
| 352 | { |
| 353 | return $this->reflClass; |
| 354 | } |
| 355 | public function enableCache(array $cache) |
| 356 | { |
| 357 | if (!isset($cache['usage'])) { |
| 358 | $cache['usage'] = self::CACHE_USAGE_READ_ONLY; |
| 359 | } |
| 360 | if (!isset($cache['region'])) { |
| 361 | $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)); |
| 362 | } |
| 363 | $this->cache = $cache; |
| 364 | } |
| 365 | public function enableAssociationCache($fieldName, array $cache) |
| 366 | { |
| 367 | $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults($fieldName, $cache); |
| 368 | } |
| 369 | public function getAssociationCacheDefaults($fieldName, array $cache) |
| 370 | { |
| 371 | if (!isset($cache['usage'])) { |
| 372 | $cache['usage'] = $this->cache['usage'] ?? self::CACHE_USAGE_READ_ONLY; |
| 373 | } |
| 374 | if (!isset($cache['region'])) { |
| 375 | $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName; |
| 376 | } |
| 377 | return $cache; |
| 378 | } |
| 379 | public function setChangeTrackingPolicy($policy) |
| 380 | { |
| 381 | $this->changeTrackingPolicy = $policy; |
| 382 | } |
| 383 | public function isChangeTrackingDeferredExplicit() |
| 384 | { |
| 385 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_EXPLICIT; |
| 386 | } |
| 387 | public function isChangeTrackingDeferredImplicit() |
| 388 | { |
| 389 | return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; |
| 390 | } |
| 391 | public function isChangeTrackingNotify() |
| 392 | { |
| 393 | return $this->changeTrackingPolicy === self::CHANGETRACKING_NOTIFY; |
| 394 | } |
| 395 | public function isIdentifier($fieldName) |
| 396 | { |
| 397 | if (!$this->identifier) { |
| 398 | return \false; |
| 399 | } |
| 400 | if (!$this->isIdentifierComposite) { |
| 401 | return $fieldName === $this->identifier[0]; |
| 402 | } |
| 403 | return in_array($fieldName, $this->identifier, \true); |
| 404 | } |
| 405 | public function isUniqueField($fieldName) |
| 406 | { |
| 407 | $mapping = $this->getFieldMapping($fieldName); |
| 408 | return $mapping !== \false && isset($mapping['unique']) && $mapping['unique']; |
| 409 | } |
| 410 | public function isNullable($fieldName) |
| 411 | { |
| 412 | $mapping = $this->getFieldMapping($fieldName); |
| 413 | return $mapping !== \false && isset($mapping['nullable']) && $mapping['nullable']; |
| 414 | } |
| 415 | public function getColumnName($fieldName) |
| 416 | { |
| 417 | // @phpstan-ignore property.deprecated |
| 418 | return $this->columnNames[$fieldName] ?? $fieldName; |
| 419 | } |
| 420 | public function getFieldMapping($fieldName) |
| 421 | { |
| 422 | if (!isset($this->fieldMappings[$fieldName])) { |
| 423 | throw MappingException::mappingNotFound($this->name, $fieldName); |
| 424 | } |
| 425 | return $this->fieldMappings[$fieldName]; |
| 426 | } |
| 427 | public function getAssociationMapping($fieldName) |
| 428 | { |
| 429 | if (!isset($this->associationMappings[$fieldName])) { |
| 430 | throw MappingException::mappingNotFound($this->name, $fieldName); |
| 431 | } |
| 432 | return $this->associationMappings[$fieldName]; |
| 433 | } |
| 434 | public function getAssociationMappings() |
| 435 | { |
| 436 | return $this->associationMappings; |
| 437 | } |
| 438 | public function getFieldName($columnName) |
| 439 | { |
| 440 | return $this->fieldNames[$columnName] ?? $columnName; |
| 441 | } |
| 442 | public function getNamedQuery($queryName) |
| 443 | { |
| 444 | if (!isset($this->namedQueries[$queryName])) { |
| 445 | throw MappingException::queryNotFound($this->name, $queryName); |
| 446 | } |
| 447 | return $this->namedQueries[$queryName]['dql']; |
| 448 | } |
| 449 | public function getNamedQueries() |
| 450 | { |
| 451 | return $this->namedQueries; |
| 452 | } |
| 453 | public function getNamedNativeQuery($queryName) |
| 454 | { |
| 455 | if (!isset($this->namedNativeQueries[$queryName])) { |
| 456 | throw MappingException::queryNotFound($this->name, $queryName); |
| 457 | } |
| 458 | return $this->namedNativeQueries[$queryName]; |
| 459 | } |
| 460 | public function getNamedNativeQueries() |
| 461 | { |
| 462 | return $this->namedNativeQueries; |
| 463 | } |
| 464 | public function getSqlResultSetMapping($name) |
| 465 | { |
| 466 | if (!isset($this->sqlResultSetMappings[$name])) { |
| 467 | throw MappingException::resultMappingNotFound($this->name, $name); |
| 468 | } |
| 469 | return $this->sqlResultSetMappings[$name]; |
| 470 | } |
| 471 | public function getSqlResultSetMappings() |
| 472 | { |
| 473 | return $this->sqlResultSetMappings; |
| 474 | } |
| 475 | private function isTypedProperty(string $name) : bool |
| 476 | { |
| 477 | return PHP_VERSION_ID >= 70400 && isset($this->reflClass) && $this->reflClass->hasProperty($name) && $this->reflClass->getProperty($name)->hasType(); |
| 478 | } |
| 479 | private function validateAndCompleteTypedFieldMapping(array $mapping) : array |
| 480 | { |
| 481 | $field = $this->reflClass->getProperty($mapping['fieldName']); |
| 482 | $mapping = $this->typedFieldMapper->validateAndComplete($mapping, $field); |
| 483 | return $mapping; |
| 484 | } |
| 485 | private function validateAndCompleteTypedAssociationMapping(array $mapping) : array |
| 486 | { |
| 487 | $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); |
| 488 | if ($type === null || ($mapping['type'] & self::TO_ONE) === 0) { |
| 489 | return $mapping; |
| 490 | } |
| 491 | if (!isset($mapping['targetEntity']) && $type instanceof ReflectionNamedType) { |
| 492 | $mapping['targetEntity'] = $type->getName(); |
| 493 | } |
| 494 | return $mapping; |
| 495 | } |
| 496 | protected function validateAndCompleteFieldMapping(array $mapping) : array |
| 497 | { |
| 498 | // Check mandatory fields |
| 499 | if (!isset($mapping['fieldName']) || !$mapping['fieldName']) { |
| 500 | throw MappingException::missingFieldName($this->name); |
| 501 | } |
| 502 | if ($this->isTypedProperty($mapping['fieldName'])) { |
| 503 | $mapping = $this->validateAndCompleteTypedFieldMapping($mapping); |
| 504 | } |
| 505 | if (!isset($mapping['type'])) { |
| 506 | // Default to string |
| 507 | $mapping['type'] = 'string'; |
| 508 | } |
| 509 | // Complete fieldName and columnName mapping |
| 510 | if (!isset($mapping['columnName'])) { |
| 511 | $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name); |
| 512 | } |
| 513 | if ($mapping['columnName'][0] === '`') { |
| 514 | $mapping['columnName'] = trim($mapping['columnName'], '`'); |
| 515 | $mapping['quoted'] = \true; |
| 516 | } |
| 517 | // @phpstan-ignore property.deprecated |
| 518 | $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; |
| 519 | if (isset($this->fieldNames[$mapping['columnName']]) || $this->discriminatorColumn && $this->discriminatorColumn['name'] === $mapping['columnName']) { |
| 520 | throw MappingException::duplicateColumnName($this->name, $mapping['columnName']); |
| 521 | } |
| 522 | $this->fieldNames[$mapping['columnName']] = $mapping['fieldName']; |
| 523 | // Complete id mapping |
| 524 | if (isset($mapping['id']) && $mapping['id'] === \true) { |
| 525 | if ($this->versionField === $mapping['fieldName']) { |
| 526 | throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']); |
| 527 | } |
| 528 | if (!in_array($mapping['fieldName'], $this->identifier, \true)) { |
| 529 | $this->identifier[] = $mapping['fieldName']; |
| 530 | } |
| 531 | // Check for composite key |
| 532 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
| 533 | $this->isIdentifierComposite = \true; |
| 534 | } |
| 535 | } |
| 536 | // @phpstan-ignore method.deprecated |
| 537 | if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) { |
| 538 | if (isset($mapping['id']) && $mapping['id'] === \true) { |
| 539 | throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']); |
| 540 | } |
| 541 | $mapping['requireSQLConversion'] = \true; |
| 542 | } |
| 543 | if (isset($mapping['generated'])) { |
| 544 | if (!in_array($mapping['generated'], [self::GENERATED_NEVER, self::GENERATED_INSERT, self::GENERATED_ALWAYS])) { |
| 545 | throw MappingException::invalidGeneratedMode($mapping['generated']); |
| 546 | } |
| 547 | if ($mapping['generated'] === self::GENERATED_NEVER) { |
| 548 | unset($mapping['generated']); |
| 549 | } |
| 550 | } |
| 551 | if (isset($mapping['enumType'])) { |
| 552 | if (PHP_VERSION_ID < 80100) { |
| 553 | throw MappingException::enumsRequirePhp81($this->name, $mapping['fieldName']); |
| 554 | } |
| 555 | if (!enum_exists($mapping['enumType'])) { |
| 556 | throw MappingException::nonEnumTypeMapped($this->name, $mapping['fieldName'], $mapping['enumType']); |
| 557 | } |
| 558 | if (!empty($mapping['id'])) { |
| 559 | $this->containsEnumIdentifier = \true; |
| 560 | } |
| 561 | } |
| 562 | return $mapping; |
| 563 | } |
| 564 | protected function _validateAndCompleteAssociationMapping(array $mapping) |
| 565 | { |
| 566 | if (!isset($mapping['mappedBy'])) { |
| 567 | $mapping['mappedBy'] = null; |
| 568 | } |
| 569 | if (!isset($mapping['inversedBy'])) { |
| 570 | $mapping['inversedBy'] = null; |
| 571 | } |
| 572 | $mapping['isOwningSide'] = \true; |
| 573 | // assume owning side until we hit mappedBy |
| 574 | if (empty($mapping['indexBy'])) { |
| 575 | unset($mapping['indexBy']); |
| 576 | } |
| 577 | // If targetEntity is unqualified, assume it is in the same namespace as |
| 578 | // the sourceEntity. |
| 579 | $mapping['sourceEntity'] = $this->name; |
| 580 | if ($this->isTypedProperty($mapping['fieldName'])) { |
| 581 | $mapping = $this->validateAndCompleteTypedAssociationMapping($mapping); |
| 582 | } |
| 583 | if (isset($mapping['targetEntity'])) { |
| 584 | $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']); |
| 585 | $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\'); |
| 586 | } |
| 587 | if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { |
| 588 | throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']); |
| 589 | } |
| 590 | // Complete id mapping |
| 591 | if (isset($mapping['id']) && $mapping['id'] === \true) { |
| 592 | if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) { |
| 593 | throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']); |
| 594 | } |
| 595 | if (!in_array($mapping['fieldName'], $this->identifier, \true)) { |
| 596 | if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) { |
| 597 | throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId($mapping['targetEntity'], $this->name, $mapping['fieldName']); |
| 598 | } |
| 599 | $this->identifier[] = $mapping['fieldName']; |
| 600 | $this->containsForeignIdentifier = \true; |
| 601 | } |
| 602 | // Check for composite key |
| 603 | if (!$this->isIdentifierComposite && count($this->identifier) > 1) { |
| 604 | $this->isIdentifierComposite = \true; |
| 605 | } |
| 606 | if ($this->cache && !isset($mapping['cache'])) { |
| 607 | throw NonCacheableEntityAssociation::fromEntityAndField($this->name, $mapping['fieldName']); |
| 608 | } |
| 609 | } |
| 610 | // Mandatory attributes for both sides |
| 611 | // Mandatory: fieldName, targetEntity |
| 612 | if (!isset($mapping['fieldName']) || !$mapping['fieldName']) { |
| 613 | throw MappingException::missingFieldName($this->name); |
| 614 | } |
| 615 | if (!isset($mapping['targetEntity'])) { |
| 616 | throw MappingException::missingTargetEntity($mapping['fieldName']); |
| 617 | } |
| 618 | // Mandatory and optional attributes for either side |
| 619 | if (!$mapping['mappedBy']) { |
| 620 | if (isset($mapping['joinTable']) && $mapping['joinTable']) { |
| 621 | if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') { |
| 622 | $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); |
| 623 | $mapping['joinTable']['quoted'] = \true; |
| 624 | } |
| 625 | } |
| 626 | } else { |
| 627 | $mapping['isOwningSide'] = \false; |
| 628 | } |
| 629 | if (isset($mapping['id']) && $mapping['id'] === \true && $mapping['type'] & self::TO_MANY) { |
| 630 | throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']); |
| 631 | } |
| 632 | // Fetch mode. Default fetch mode to LAZY, if not set. |
| 633 | if (!isset($mapping['fetch'])) { |
| 634 | $mapping['fetch'] = self::FETCH_LAZY; |
| 635 | } |
| 636 | // Cascades |
| 637 | $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : []; |
| 638 | $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach']; |
| 639 | if (in_array('all', $cascades, \true)) { |
| 640 | $cascades = $allCascades; |
| 641 | } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) { |
| 642 | throw MappingException::invalidCascadeOption(array_diff($cascades, $allCascades), $this->name, $mapping['fieldName']); |
| 643 | } |
| 644 | $mapping['cascade'] = $cascades; |
| 645 | $mapping['isCascadeRemove'] = in_array('remove', $cascades, \true); |
| 646 | $mapping['isCascadePersist'] = in_array('persist', $cascades, \true); |
| 647 | $mapping['isCascadeRefresh'] = in_array('refresh', $cascades, \true); |
| 648 | $mapping['isCascadeMerge'] = in_array('merge', $cascades, \true); |
| 649 | $mapping['isCascadeDetach'] = in_array('detach', $cascades, \true); |
| 650 | return $mapping; |
| 651 | } |
| 652 | protected function _validateAndCompleteOneToOneMapping(array $mapping) |
| 653 | { |
| 654 | $mapping = $this->_validateAndCompleteAssociationMapping($mapping); |
| 655 | if (isset($mapping['joinColumns']) && $mapping['joinColumns'] && !$mapping['isOwningSide']) { |
| 656 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/10654', 'JoinColumn configuration is not allowed on the inverse side of one-to-one associations, and will throw a MappingException in Doctrine ORM 3.0'); |
| 657 | } |
| 658 | if (isset($mapping['joinColumns']) && $mapping['joinColumns']) { |
| 659 | $mapping['isOwningSide'] = \true; |
| 660 | } |
| 661 | if ($mapping['isOwningSide']) { |
| 662 | if (empty($mapping['joinColumns'])) { |
| 663 | // Apply default join column |
| 664 | $mapping['joinColumns'] = [['name' => $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name), 'referencedColumnName' => $this->namingStrategy->referenceColumnName()]]; |
| 665 | } |
| 666 | $uniqueConstraintColumns = []; |
| 667 | foreach ($mapping['joinColumns'] as &$joinColumn) { |
| 668 | if ($mapping['type'] === self::ONE_TO_ONE && !$this->isInheritanceTypeSingleTable()) { |
| 669 | if (count($mapping['joinColumns']) === 1) { |
| 670 | if (empty($mapping['id'])) { |
| 671 | $joinColumn['unique'] = \true; |
| 672 | } |
| 673 | } else { |
| 674 | $uniqueConstraintColumns[] = $joinColumn['name']; |
| 675 | } |
| 676 | } |
| 677 | if (empty($joinColumn['name'])) { |
| 678 | $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name); |
| 679 | } |
| 680 | if (empty($joinColumn['referencedColumnName'])) { |
| 681 | $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); |
| 682 | } |
| 683 | if ($joinColumn['name'][0] === '`') { |
| 684 | $joinColumn['name'] = trim($joinColumn['name'], '`'); |
| 685 | $joinColumn['quoted'] = \true; |
| 686 | } |
| 687 | if ($joinColumn['referencedColumnName'][0] === '`') { |
| 688 | $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); |
| 689 | $joinColumn['quoted'] = \true; |
| 690 | } |
| 691 | $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; |
| 692 | $mapping['joinColumnFieldNames'][$joinColumn['name']] = $joinColumn['fieldName'] ?? $joinColumn['name']; |
| 693 | } |
| 694 | if ($uniqueConstraintColumns) { |
| 695 | if (!$this->table) { |
| 696 | throw new RuntimeException('ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.'); |
| 697 | } |
| 698 | $this->table['uniqueConstraints'][$mapping['fieldName'] . '_uniq'] = ['columns' => $uniqueConstraintColumns]; |
| 699 | } |
| 700 | $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']); |
| 701 | } |
| 702 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
| 703 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
| 704 | if ($mapping['orphanRemoval']) { |
| 705 | unset($mapping['unique']); |
| 706 | } |
| 707 | if (isset($mapping['id']) && $mapping['id'] === \true && !$mapping['isOwningSide']) { |
| 708 | throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']); |
| 709 | } |
| 710 | return $mapping; |
| 711 | } |
| 712 | protected function _validateAndCompleteOneToManyMapping(array $mapping) |
| 713 | { |
| 714 | $mapping = $this->_validateAndCompleteAssociationMapping($mapping); |
| 715 | // OneToMany-side MUST be inverse (must have mappedBy) |
| 716 | if (!isset($mapping['mappedBy'])) { |
| 717 | throw MappingException::oneToManyRequiresMappedBy($this->name, $mapping['fieldName']); |
| 718 | } |
| 719 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
| 720 | $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove']; |
| 721 | $this->assertMappingOrderBy($mapping); |
| 722 | return $mapping; |
| 723 | } |
| 724 | protected function _validateAndCompleteManyToManyMapping(array $mapping) |
| 725 | { |
| 726 | $mapping = $this->_validateAndCompleteAssociationMapping($mapping); |
| 727 | if ($mapping['isOwningSide']) { |
| 728 | // owning side MUST have a join table |
| 729 | if (!isset($mapping['joinTable']['name'])) { |
| 730 | $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']); |
| 731 | } |
| 732 | $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] === $mapping['targetEntity'] && !(isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])); |
| 733 | if (!isset($mapping['joinTable']['joinColumns'])) { |
| 734 | $mapping['joinTable']['joinColumns'] = [['name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null), 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), 'onDelete' => 'CASCADE']]; |
| 735 | } |
| 736 | if (!isset($mapping['joinTable']['inverseJoinColumns'])) { |
| 737 | $mapping['joinTable']['inverseJoinColumns'] = [['name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null), 'referencedColumnName' => $this->namingStrategy->referenceColumnName(), 'onDelete' => 'CASCADE']]; |
| 738 | } |
| 739 | $mapping['joinTableColumns'] = []; |
| 740 | foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { |
| 741 | if (empty($joinColumn['name'])) { |
| 742 | $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']); |
| 743 | } |
| 744 | if (empty($joinColumn['referencedColumnName'])) { |
| 745 | $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); |
| 746 | } |
| 747 | if ($joinColumn['name'][0] === '`') { |
| 748 | $joinColumn['name'] = trim($joinColumn['name'], '`'); |
| 749 | $joinColumn['quoted'] = \true; |
| 750 | } |
| 751 | if ($joinColumn['referencedColumnName'][0] === '`') { |
| 752 | $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`'); |
| 753 | $joinColumn['quoted'] = \true; |
| 754 | } |
| 755 | if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) === 'cascade') { |
| 756 | $mapping['isOnDeleteCascade'] = \true; |
| 757 | } |
| 758 | $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName']; |
| 759 | $mapping['joinTableColumns'][] = $joinColumn['name']; |
| 760 | } |
| 761 | foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { |
| 762 | if (empty($inverseJoinColumn['name'])) { |
| 763 | $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']); |
| 764 | } |
| 765 | if (empty($inverseJoinColumn['referencedColumnName'])) { |
| 766 | $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName(); |
| 767 | } |
| 768 | if ($inverseJoinColumn['name'][0] === '`') { |
| 769 | $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); |
| 770 | $inverseJoinColumn['quoted'] = \true; |
| 771 | } |
| 772 | if ($inverseJoinColumn['referencedColumnName'][0] === '`') { |
| 773 | $inverseJoinColumn['referencedColumnName'] = trim($inverseJoinColumn['referencedColumnName'], '`'); |
| 774 | $inverseJoinColumn['quoted'] = \true; |
| 775 | } |
| 776 | if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) === 'cascade') { |
| 777 | $mapping['isOnDeleteCascade'] = \true; |
| 778 | } |
| 779 | $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName']; |
| 780 | $mapping['joinTableColumns'][] = $inverseJoinColumn['name']; |
| 781 | } |
| 782 | } |
| 783 | $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']; |
| 784 | $this->assertMappingOrderBy($mapping); |
| 785 | return $mapping; |
| 786 | } |
| 787 | public function getIdentifierFieldNames() |
| 788 | { |
| 789 | return $this->identifier; |
| 790 | } |
| 791 | public function getSingleIdentifierFieldName() |
| 792 | { |
| 793 | if ($this->isIdentifierComposite) { |
| 794 | throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name); |
| 795 | } |
| 796 | if (!isset($this->identifier[0])) { |
| 797 | throw MappingException::noIdDefined($this->name); |
| 798 | } |
| 799 | return $this->identifier[0]; |
| 800 | } |
| 801 | public function getSingleIdentifierColumnName() |
| 802 | { |
| 803 | return $this->getColumnName($this->getSingleIdentifierFieldName()); |
| 804 | } |
| 805 | public function setIdentifier(array $identifier) |
| 806 | { |
| 807 | $this->identifier = $identifier; |
| 808 | $this->isIdentifierComposite = count($this->identifier) > 1; |
| 809 | } |
| 810 | public function getIdentifier() |
| 811 | { |
| 812 | return $this->identifier; |
| 813 | } |
| 814 | public function hasField($fieldName) |
| 815 | { |
| 816 | return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]); |
| 817 | } |
| 818 | public function getColumnNames(?array $fieldNames = null) |
| 819 | { |
| 820 | if ($fieldNames === null) { |
| 821 | return array_keys($this->fieldNames); |
| 822 | } |
| 823 | return array_values(array_map([$this, 'getColumnName'], $fieldNames)); |
| 824 | } |
| 825 | public function getIdentifierColumnNames() |
| 826 | { |
| 827 | $columnNames = []; |
| 828 | foreach ($this->identifier as $idProperty) { |
| 829 | if (isset($this->fieldMappings[$idProperty])) { |
| 830 | $columnNames[] = $this->fieldMappings[$idProperty]['columnName']; |
| 831 | continue; |
| 832 | } |
| 833 | // Association defined as Id field |
| 834 | $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; |
| 835 | $assocColumnNames = array_map(static function ($joinColumn) { |
| 836 | return $joinColumn['name']; |
| 837 | }, $joinColumns); |
| 838 | $columnNames = array_merge($columnNames, $assocColumnNames); |
| 839 | } |
| 840 | return $columnNames; |
| 841 | } |
| 842 | public function setIdGeneratorType($generatorType) |
| 843 | { |
| 844 | $this->generatorType = $generatorType; |
| 845 | } |
| 846 | public function usesIdGenerator() |
| 847 | { |
| 848 | return $this->generatorType !== self::GENERATOR_TYPE_NONE; |
| 849 | } |
| 850 | public function isInheritanceTypeNone() |
| 851 | { |
| 852 | return $this->inheritanceType === self::INHERITANCE_TYPE_NONE; |
| 853 | } |
| 854 | public function isInheritanceTypeJoined() |
| 855 | { |
| 856 | return $this->inheritanceType === self::INHERITANCE_TYPE_JOINED; |
| 857 | } |
| 858 | public function isInheritanceTypeSingleTable() |
| 859 | { |
| 860 | return $this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_TABLE; |
| 861 | } |
| 862 | public function isInheritanceTypeTablePerClass() |
| 863 | { |
| 864 | Deprecation::triggerIfCalledFromOutside('doctrine/orm', 'https://github.com/doctrine/orm/pull/10414/', 'Concrete table inheritance has never been implemented, and its stubs will be removed in Doctrine ORM 3.0 with no replacement'); |
| 865 | return $this->inheritanceType === self::INHERITANCE_TYPE_TABLE_PER_CLASS; |
| 866 | } |
| 867 | public function isIdGeneratorIdentity() |
| 868 | { |
| 869 | return $this->generatorType === self::GENERATOR_TYPE_IDENTITY; |
| 870 | } |
| 871 | public function isIdGeneratorSequence() |
| 872 | { |
| 873 | return $this->generatorType === self::GENERATOR_TYPE_SEQUENCE; |
| 874 | } |
| 875 | public function isIdGeneratorTable() |
| 876 | { |
| 877 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/9046', '%s is deprecated', __METHOD__); |
| 878 | return \false; |
| 879 | } |
| 880 | public function isIdentifierNatural() |
| 881 | { |
| 882 | return $this->generatorType === self::GENERATOR_TYPE_NONE; |
| 883 | } |
| 884 | public function isIdentifierUuid() |
| 885 | { |
| 886 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/9046', '%s is deprecated', __METHOD__); |
| 887 | return $this->generatorType === self::GENERATOR_TYPE_UUID; |
| 888 | } |
| 889 | public function getTypeOfField($fieldName) |
| 890 | { |
| 891 | return isset($this->fieldMappings[$fieldName]) ? $this->fieldMappings[$fieldName]['type'] : null; |
| 892 | } |
| 893 | public function getTypeOfColumn($columnName) |
| 894 | { |
| 895 | return $this->getTypeOfField($this->getFieldName($columnName)); |
| 896 | } |
| 897 | public function getTableName() |
| 898 | { |
| 899 | return $this->table['name']; |
| 900 | } |
| 901 | public function getSchemaName() |
| 902 | { |
| 903 | return $this->table['schema'] ?? null; |
| 904 | } |
| 905 | public function getTemporaryIdTableName() |
| 906 | { |
| 907 | // replace dots with underscores because PostgreSQL creates temporary tables in a special schema |
| 908 | return str_replace('.', '_', $this->getTableName() . '_id_tmp'); |
| 909 | } |
| 910 | public function setSubclasses(array $subclasses) |
| 911 | { |
| 912 | foreach ($subclasses as $subclass) { |
| 913 | $this->subClasses[] = $this->fullyQualifiedClassName($subclass); |
| 914 | } |
| 915 | } |
| 916 | public function setParentClasses(array $classNames) |
| 917 | { |
| 918 | $this->parentClasses = $classNames; |
| 919 | if (count($classNames) > 0) { |
| 920 | $this->rootEntityName = array_pop($classNames); |
| 921 | } |
| 922 | } |
| 923 | public function setInheritanceType($type) |
| 924 | { |
| 925 | if (!$this->isInheritanceType($type)) { |
| 926 | throw MappingException::invalidInheritanceType($this->name, $type); |
| 927 | } |
| 928 | $this->inheritanceType = $type; |
| 929 | } |
| 930 | public function setAssociationOverride($fieldName, array $overrideMapping) |
| 931 | { |
| 932 | if (!isset($this->associationMappings[$fieldName])) { |
| 933 | throw MappingException::invalidOverrideFieldName($this->name, $fieldName); |
| 934 | } |
| 935 | $mapping = $this->associationMappings[$fieldName]; |
| 936 | if (isset($mapping['inherited'])) { |
| 937 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/10470', 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s. This is a misconfiguration and will be an error in Doctrine ORM 3.0.', $this->name, $fieldName, $mapping['inherited']); |
| 938 | } |
| 939 | if (isset($overrideMapping['joinColumns'])) { |
| 940 | $mapping['joinColumns'] = $overrideMapping['joinColumns']; |
| 941 | } |
| 942 | if (isset($overrideMapping['inversedBy'])) { |
| 943 | $mapping['inversedBy'] = $overrideMapping['inversedBy']; |
| 944 | } |
| 945 | if (isset($overrideMapping['joinTable'])) { |
| 946 | $mapping['joinTable'] = $overrideMapping['joinTable']; |
| 947 | } |
| 948 | if (isset($overrideMapping['fetch'])) { |
| 949 | $mapping['fetch'] = $overrideMapping['fetch']; |
| 950 | } |
| 951 | $mapping['joinColumnFieldNames'] = null; |
| 952 | $mapping['joinTableColumns'] = null; |
| 953 | $mapping['sourceToTargetKeyColumns'] = null; |
| 954 | $mapping['relationToSourceKeyColumns'] = null; |
| 955 | $mapping['relationToTargetKeyColumns'] = null; |
| 956 | switch ($mapping['type']) { |
| 957 | case self::ONE_TO_ONE: |
| 958 | $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); |
| 959 | break; |
| 960 | case self::ONE_TO_MANY: |
| 961 | $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); |
| 962 | break; |
| 963 | case self::MANY_TO_ONE: |
| 964 | $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); |
| 965 | break; |
| 966 | case self::MANY_TO_MANY: |
| 967 | $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); |
| 968 | break; |
| 969 | } |
| 970 | $this->associationMappings[$fieldName] = $mapping; |
| 971 | } |
| 972 | public function setAttributeOverride($fieldName, array $overrideMapping) |
| 973 | { |
| 974 | if (!isset($this->fieldMappings[$fieldName])) { |
| 975 | throw MappingException::invalidOverrideFieldName($this->name, $fieldName); |
| 976 | } |
| 977 | $mapping = $this->fieldMappings[$fieldName]; |
| 978 | if (isset($mapping['inherited'])) { |
| 979 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/10470', 'Overrides are only allowed for fields or associations declared in mapped superclasses or traits. This is not the case for %s::%s, which was inherited from %s. This is a misconfiguration and will be an error in Doctrine ORM 3.0.', $this->name, $fieldName, $mapping['inherited']); |
| 980 | //throw MappingException::illegalOverrideOfInheritedProperty($this->name, $fieldName); |
| 981 | } |
| 982 | if (isset($mapping['id'])) { |
| 983 | $overrideMapping['id'] = $mapping['id']; |
| 984 | } |
| 985 | if (isset($mapping['declared'])) { |
| 986 | $overrideMapping['declared'] = $mapping['declared']; |
| 987 | } |
| 988 | if (!isset($overrideMapping['type'])) { |
| 989 | $overrideMapping['type'] = $mapping['type']; |
| 990 | } |
| 991 | if (!isset($overrideMapping['fieldName'])) { |
| 992 | $overrideMapping['fieldName'] = $mapping['fieldName']; |
| 993 | } |
| 994 | if ($overrideMapping['type'] !== $mapping['type']) { |
| 995 | throw MappingException::invalidOverrideFieldType($this->name, $fieldName); |
| 996 | } |
| 997 | unset($this->fieldMappings[$fieldName]); |
| 998 | unset($this->fieldNames[$mapping['columnName']]); |
| 999 | // @phpstan-ignore property.deprecated |
| 1000 | unset($this->columnNames[$mapping['fieldName']]); |
| 1001 | $overrideMapping = $this->validateAndCompleteFieldMapping($overrideMapping); |
| 1002 | $this->fieldMappings[$fieldName] = $overrideMapping; |
| 1003 | } |
| 1004 | public function isInheritedField($fieldName) |
| 1005 | { |
| 1006 | return isset($this->fieldMappings[$fieldName]['inherited']); |
| 1007 | } |
| 1008 | public function isRootEntity() |
| 1009 | { |
| 1010 | return $this->name === $this->rootEntityName; |
| 1011 | } |
| 1012 | public function isInheritedAssociation($fieldName) |
| 1013 | { |
| 1014 | return isset($this->associationMappings[$fieldName]['inherited']); |
| 1015 | } |
| 1016 | public function isInheritedEmbeddedClass($fieldName) |
| 1017 | { |
| 1018 | return isset($this->embeddedClasses[$fieldName]['inherited']); |
| 1019 | } |
| 1020 | public function setTableName($tableName) |
| 1021 | { |
| 1022 | $this->table['name'] = $tableName; |
| 1023 | } |
| 1024 | public function setPrimaryTable(array $table) |
| 1025 | { |
| 1026 | if (isset($table['name'])) { |
| 1027 | // Split schema and table name from a table name like "myschema.mytable" |
| 1028 | if (str_contains($table['name'], '.')) { |
| 1029 | [$this->table['schema'], $table['name']] = explode('.', $table['name'], 2); |
| 1030 | } |
| 1031 | if ($table['name'][0] === '`') { |
| 1032 | $table['name'] = trim($table['name'], '`'); |
| 1033 | $this->table['quoted'] = \true; |
| 1034 | } |
| 1035 | $this->table['name'] = $table['name']; |
| 1036 | } |
| 1037 | if (isset($table['quoted'])) { |
| 1038 | $this->table['quoted'] = $table['quoted']; |
| 1039 | } |
| 1040 | if (isset($table['schema'])) { |
| 1041 | $this->table['schema'] = $table['schema']; |
| 1042 | } |
| 1043 | if (isset($table['indexes'])) { |
| 1044 | $this->table['indexes'] = $table['indexes']; |
| 1045 | } |
| 1046 | if (isset($table['uniqueConstraints'])) { |
| 1047 | $this->table['uniqueConstraints'] = $table['uniqueConstraints']; |
| 1048 | } |
| 1049 | if (isset($table['options'])) { |
| 1050 | $this->table['options'] = $table['options']; |
| 1051 | } |
| 1052 | } |
| 1053 | private function isInheritanceType(int $type) : bool |
| 1054 | { |
| 1055 | // @phpstan-ignore classConstant.deprecated |
| 1056 | if ($type === self::INHERITANCE_TYPE_TABLE_PER_CLASS) { |
| 1057 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/10414/', 'Concrete table inheritance has never been implemented, and its stubs will be removed in Doctrine ORM 3.0 with no replacement'); |
| 1058 | } |
| 1059 | return $type === self::INHERITANCE_TYPE_NONE || $type === self::INHERITANCE_TYPE_SINGLE_TABLE || $type === self::INHERITANCE_TYPE_JOINED || $type === self::INHERITANCE_TYPE_TABLE_PER_CLASS; |
| 1060 | } |
| 1061 | public function mapField(array $mapping) |
| 1062 | { |
| 1063 | $mapping = $this->validateAndCompleteFieldMapping($mapping); |
| 1064 | $this->assertFieldNotMapped($mapping['fieldName']); |
| 1065 | if (isset($mapping['generated'])) { |
| 1066 | $this->requiresFetchAfterChange = \true; |
| 1067 | } |
| 1068 | $this->fieldMappings[$mapping['fieldName']] = $mapping; |
| 1069 | } |
| 1070 | public function addInheritedAssociationMapping(array $mapping) |
| 1071 | { |
| 1072 | if (isset($this->associationMappings[$mapping['fieldName']])) { |
| 1073 | throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']); |
| 1074 | } |
| 1075 | $this->associationMappings[$mapping['fieldName']] = $mapping; |
| 1076 | } |
| 1077 | public function addInheritedFieldMapping(array $fieldMapping) |
| 1078 | { |
| 1079 | $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping; |
| 1080 | // @phpstan-ignore property.deprecated |
| 1081 | $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName']; |
| 1082 | $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName']; |
| 1083 | if (isset($fieldMapping['generated'])) { |
| 1084 | $this->requiresFetchAfterChange = \true; |
| 1085 | } |
| 1086 | } |
| 1087 | public function addNamedQuery(array $queryMapping) |
| 1088 | { |
| 1089 | if (!isset($queryMapping['name'])) { |
| 1090 | throw MappingException::nameIsMandatoryForQueryMapping($this->name); |
| 1091 | } |
| 1092 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8592', 'Named Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', $queryMapping['name'], $this->name); |
| 1093 | if (isset($this->namedQueries[$queryMapping['name']])) { |
| 1094 | throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); |
| 1095 | } |
| 1096 | if (!isset($queryMapping['query'])) { |
| 1097 | throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); |
| 1098 | } |
| 1099 | $name = $queryMapping['name']; |
| 1100 | $query = $queryMapping['query']; |
| 1101 | $dql = str_replace('__CLASS__', $this->name, $query); |
| 1102 | $this->namedQueries[$name] = ['name' => $name, 'query' => $query, 'dql' => $dql]; |
| 1103 | } |
| 1104 | public function addNamedNativeQuery(array $queryMapping) |
| 1105 | { |
| 1106 | if (!isset($queryMapping['name'])) { |
| 1107 | throw MappingException::nameIsMandatoryForQueryMapping($this->name); |
| 1108 | } |
| 1109 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8592', 'Named Native Queries are deprecated, here "%s" on entity %s. Move the query logic into EntityRepository', $queryMapping['name'], $this->name); |
| 1110 | if (isset($this->namedNativeQueries[$queryMapping['name']])) { |
| 1111 | throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']); |
| 1112 | } |
| 1113 | if (!isset($queryMapping['query'])) { |
| 1114 | throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']); |
| 1115 | } |
| 1116 | if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) { |
| 1117 | throw MappingException::missingQueryMapping($this->name, $queryMapping['name']); |
| 1118 | } |
| 1119 | $queryMapping['isSelfClass'] = \false; |
| 1120 | if (isset($queryMapping['resultClass'])) { |
| 1121 | if ($queryMapping['resultClass'] === '__CLASS__') { |
| 1122 | $queryMapping['isSelfClass'] = \true; |
| 1123 | $queryMapping['resultClass'] = $this->name; |
| 1124 | } |
| 1125 | $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']); |
| 1126 | $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\'); |
| 1127 | } |
| 1128 | $this->namedNativeQueries[$queryMapping['name']] = $queryMapping; |
| 1129 | } |
| 1130 | public function addSqlResultSetMapping(array $resultMapping) |
| 1131 | { |
| 1132 | if (!isset($resultMapping['name'])) { |
| 1133 | throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name); |
| 1134 | } |
| 1135 | if (isset($this->sqlResultSetMappings[$resultMapping['name']])) { |
| 1136 | throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']); |
| 1137 | } |
| 1138 | if (isset($resultMapping['entities'])) { |
| 1139 | foreach ($resultMapping['entities'] as $key => $entityResult) { |
| 1140 | if (!isset($entityResult['entityClass'])) { |
| 1141 | throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']); |
| 1142 | } |
| 1143 | $entityResult['isSelfClass'] = \false; |
| 1144 | if ($entityResult['entityClass'] === '__CLASS__') { |
| 1145 | $entityResult['isSelfClass'] = \true; |
| 1146 | $entityResult['entityClass'] = $this->name; |
| 1147 | } |
| 1148 | $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']); |
| 1149 | $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\'); |
| 1150 | $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass']; |
| 1151 | if (isset($entityResult['fields'])) { |
| 1152 | foreach ($entityResult['fields'] as $k => $field) { |
| 1153 | if (!isset($field['name'])) { |
| 1154 | throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']); |
| 1155 | } |
| 1156 | if (!isset($field['column'])) { |
| 1157 | $fieldName = $field['name']; |
| 1158 | if (str_contains($fieldName, '.')) { |
| 1159 | [, $fieldName] = explode('.', $fieldName); |
| 1160 | } |
| 1161 | $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName; |
| 1162 | } |
| 1163 | } |
| 1164 | } |
| 1165 | } |
| 1166 | } |
| 1167 | $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping; |
| 1168 | } |
| 1169 | public function mapOneToOne(array $mapping) |
| 1170 | { |
| 1171 | $mapping['type'] = self::ONE_TO_ONE; |
| 1172 | $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); |
| 1173 | $this->_storeAssociationMapping($mapping); |
| 1174 | } |
| 1175 | public function mapOneToMany(array $mapping) |
| 1176 | { |
| 1177 | $mapping['type'] = self::ONE_TO_MANY; |
| 1178 | $mapping = $this->_validateAndCompleteOneToManyMapping($mapping); |
| 1179 | $this->_storeAssociationMapping($mapping); |
| 1180 | } |
| 1181 | public function mapManyToOne(array $mapping) |
| 1182 | { |
| 1183 | $mapping['type'] = self::MANY_TO_ONE; |
| 1184 | // A many-to-one mapping is essentially a one-one backreference |
| 1185 | $mapping = $this->_validateAndCompleteOneToOneMapping($mapping); |
| 1186 | $this->_storeAssociationMapping($mapping); |
| 1187 | } |
| 1188 | public function mapManyToMany(array $mapping) |
| 1189 | { |
| 1190 | $mapping['type'] = self::MANY_TO_MANY; |
| 1191 | $mapping = $this->_validateAndCompleteManyToManyMapping($mapping); |
| 1192 | $this->_storeAssociationMapping($mapping); |
| 1193 | } |
| 1194 | protected function _storeAssociationMapping(array $assocMapping) |
| 1195 | { |
| 1196 | $sourceFieldName = $assocMapping['fieldName']; |
| 1197 | $this->assertFieldNotMapped($sourceFieldName); |
| 1198 | $this->associationMappings[$sourceFieldName] = $assocMapping; |
| 1199 | } |
| 1200 | public function setCustomRepositoryClass($repositoryClassName) |
| 1201 | { |
| 1202 | $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName); |
| 1203 | } |
| 1204 | public function invokeLifecycleCallbacks($lifecycleEvent, $entity) |
| 1205 | { |
| 1206 | foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) { |
| 1207 | $entity->{$callback}(); |
| 1208 | } |
| 1209 | } |
| 1210 | public function hasLifecycleCallbacks($lifecycleEvent) |
| 1211 | { |
| 1212 | return isset($this->lifecycleCallbacks[$lifecycleEvent]); |
| 1213 | } |
| 1214 | public function getLifecycleCallbacks($event) |
| 1215 | { |
| 1216 | return $this->lifecycleCallbacks[$event] ?? []; |
| 1217 | } |
| 1218 | public function addLifecycleCallback($callback, $event) |
| 1219 | { |
| 1220 | if ($this->isEmbeddedClass) { |
| 1221 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/8381', 'Registering lifecycle callback %s on Embedded class %s is not doing anything and will throw exception in 3.0', $event, $this->name); |
| 1222 | } |
| 1223 | if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], \true)) { |
| 1224 | return; |
| 1225 | } |
| 1226 | $this->lifecycleCallbacks[$event][] = $callback; |
| 1227 | } |
| 1228 | public function setLifecycleCallbacks(array $callbacks) |
| 1229 | { |
| 1230 | $this->lifecycleCallbacks = $callbacks; |
| 1231 | } |
| 1232 | public function addEntityListener($eventName, $class, $method) |
| 1233 | { |
| 1234 | $class = $this->fullyQualifiedClassName($class); |
| 1235 | $listener = ['class' => $class, 'method' => $method]; |
| 1236 | if (!class_exists($class)) { |
| 1237 | throw MappingException::entityListenerClassNotFound($class, $this->name); |
| 1238 | } |
| 1239 | if (!method_exists($class, $method)) { |
| 1240 | throw MappingException::entityListenerMethodNotFound($class, $method, $this->name); |
| 1241 | } |
| 1242 | if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], \true)) { |
| 1243 | throw MappingException::duplicateEntityListener($class, $method, $this->name); |
| 1244 | } |
| 1245 | $this->entityListeners[$eventName][] = $listener; |
| 1246 | } |
| 1247 | public function setDiscriminatorColumn($columnDef) |
| 1248 | { |
| 1249 | if ($columnDef !== null) { |
| 1250 | if (!isset($columnDef['name'])) { |
| 1251 | throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name); |
| 1252 | } |
| 1253 | if (isset($this->fieldNames[$columnDef['name']])) { |
| 1254 | throw MappingException::duplicateColumnName($this->name, $columnDef['name']); |
| 1255 | } |
| 1256 | if (!isset($columnDef['fieldName'])) { |
| 1257 | $columnDef['fieldName'] = $columnDef['name']; |
| 1258 | } |
| 1259 | if (!isset($columnDef['type'])) { |
| 1260 | $columnDef['type'] = 'string'; |
| 1261 | } |
| 1262 | if (in_array($columnDef['type'], ['boolean', 'array', 'object', 'datetime', 'time', 'date'], \true)) { |
| 1263 | throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']); |
| 1264 | } |
| 1265 | $this->discriminatorColumn = $columnDef; |
| 1266 | } |
| 1267 | } |
| 1268 | public final function getDiscriminatorColumn() : array |
| 1269 | { |
| 1270 | if ($this->discriminatorColumn === null) { |
| 1271 | throw new LogicException('The discriminator column was not set.'); |
| 1272 | } |
| 1273 | return $this->discriminatorColumn; |
| 1274 | } |
| 1275 | public function setDiscriminatorMap(array $map) |
| 1276 | { |
| 1277 | foreach ($map as $value => $className) { |
| 1278 | $this->addDiscriminatorMapClass($value, $className); |
| 1279 | } |
| 1280 | } |
| 1281 | public function addDiscriminatorMapClass($name, $className) |
| 1282 | { |
| 1283 | $className = $this->fullyQualifiedClassName($className); |
| 1284 | $className = ltrim($className, '\\'); |
| 1285 | $this->discriminatorMap[$name] = $className; |
| 1286 | if ($this->name === $className) { |
| 1287 | $this->discriminatorValue = $name; |
| 1288 | return; |
| 1289 | } |
| 1290 | if (!(class_exists($className) || interface_exists($className))) { |
| 1291 | throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); |
| 1292 | } |
| 1293 | $this->addSubClass($className); |
| 1294 | } |
| 1295 | public function addSubClasses(array $classes) : void |
| 1296 | { |
| 1297 | foreach ($classes as $className) { |
| 1298 | $this->addSubClass($className); |
| 1299 | } |
| 1300 | } |
| 1301 | public function addSubClass(string $className) : void |
| 1302 | { |
| 1303 | // By ignoring classes that are not subclasses of the current class, we simplify inheriting |
| 1304 | // the subclass list from a parent class at the beginning of \MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadataFactory::doLoadMetadata. |
| 1305 | if (is_subclass_of($className, $this->name) && !in_array($className, $this->subClasses, \true)) { |
| 1306 | $this->subClasses[] = $className; |
| 1307 | } |
| 1308 | } |
| 1309 | public function hasNamedQuery($queryName) |
| 1310 | { |
| 1311 | return isset($this->namedQueries[$queryName]); |
| 1312 | } |
| 1313 | public function hasNamedNativeQuery($queryName) |
| 1314 | { |
| 1315 | return isset($this->namedNativeQueries[$queryName]); |
| 1316 | } |
| 1317 | public function hasSqlResultSetMapping($name) |
| 1318 | { |
| 1319 | return isset($this->sqlResultSetMappings[$name]); |
| 1320 | } |
| 1321 | public function hasAssociation($fieldName) |
| 1322 | { |
| 1323 | return isset($this->associationMappings[$fieldName]); |
| 1324 | } |
| 1325 | public function isSingleValuedAssociation($fieldName) |
| 1326 | { |
| 1327 | return isset($this->associationMappings[$fieldName]) && $this->associationMappings[$fieldName]['type'] & self::TO_ONE; |
| 1328 | } |
| 1329 | public function isCollectionValuedAssociation($fieldName) |
| 1330 | { |
| 1331 | return isset($this->associationMappings[$fieldName]) && !($this->associationMappings[$fieldName]['type'] & self::TO_ONE); |
| 1332 | } |
| 1333 | public function isAssociationWithSingleJoinColumn($fieldName) |
| 1334 | { |
| 1335 | return isset($this->associationMappings[$fieldName]) && isset($this->associationMappings[$fieldName]['joinColumns'][0]) && !isset($this->associationMappings[$fieldName]['joinColumns'][1]); |
| 1336 | } |
| 1337 | public function getSingleAssociationJoinColumnName($fieldName) |
| 1338 | { |
| 1339 | if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { |
| 1340 | throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); |
| 1341 | } |
| 1342 | return $this->associationMappings[$fieldName]['joinColumns'][0]['name']; |
| 1343 | } |
| 1344 | public function getSingleAssociationReferencedJoinColumnName($fieldName) |
| 1345 | { |
| 1346 | if (!$this->isAssociationWithSingleJoinColumn($fieldName)) { |
| 1347 | throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName); |
| 1348 | } |
| 1349 | return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName']; |
| 1350 | } |
| 1351 | public function getFieldForColumn($columnName) |
| 1352 | { |
| 1353 | if (isset($this->fieldNames[$columnName])) { |
| 1354 | return $this->fieldNames[$columnName]; |
| 1355 | } |
| 1356 | foreach ($this->associationMappings as $assocName => $mapping) { |
| 1357 | if ($this->isAssociationWithSingleJoinColumn($assocName) && $this->associationMappings[$assocName]['joinColumns'][0]['name'] === $columnName) { |
| 1358 | return $assocName; |
| 1359 | } |
| 1360 | } |
| 1361 | throw MappingException::noFieldNameFoundForColumn($this->name, $columnName); |
| 1362 | } |
| 1363 | public function setIdGenerator($generator) |
| 1364 | { |
| 1365 | $this->idGenerator = $generator; |
| 1366 | } |
| 1367 | public function setCustomGeneratorDefinition(array $definition) |
| 1368 | { |
| 1369 | $this->customGeneratorDefinition = $definition; |
| 1370 | } |
| 1371 | public function setSequenceGeneratorDefinition(array $definition) |
| 1372 | { |
| 1373 | if (!isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') { |
| 1374 | throw MappingException::missingSequenceName($this->name); |
| 1375 | } |
| 1376 | if ($definition['sequenceName'][0] === '`') { |
| 1377 | $definition['sequenceName'] = trim($definition['sequenceName'], '`'); |
| 1378 | $definition['quoted'] = \true; |
| 1379 | } |
| 1380 | if (!isset($definition['allocationSize']) || trim((string) $definition['allocationSize']) === '') { |
| 1381 | $definition['allocationSize'] = '1'; |
| 1382 | } |
| 1383 | if (!isset($definition['initialValue']) || trim((string) $definition['initialValue']) === '') { |
| 1384 | $definition['initialValue'] = '1'; |
| 1385 | } |
| 1386 | $definition['allocationSize'] = (string) $definition['allocationSize']; |
| 1387 | $definition['initialValue'] = (string) $definition['initialValue']; |
| 1388 | $this->sequenceGeneratorDefinition = $definition; |
| 1389 | } |
| 1390 | public function setVersionMapping(array &$mapping) |
| 1391 | { |
| 1392 | $this->isVersioned = \true; |
| 1393 | $this->versionField = $mapping['fieldName']; |
| 1394 | $this->requiresFetchAfterChange = \true; |
| 1395 | if (!isset($mapping['default'])) { |
| 1396 | if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'], \true)) { |
| 1397 | $mapping['default'] = 1; |
| 1398 | } elseif ($mapping['type'] === 'datetime') { |
| 1399 | $mapping['default'] = 'CURRENT_TIMESTAMP'; |
| 1400 | } else { |
| 1401 | throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']); |
| 1402 | } |
| 1403 | } |
| 1404 | } |
| 1405 | public function setVersioned($bool) |
| 1406 | { |
| 1407 | $this->isVersioned = $bool; |
| 1408 | if ($bool) { |
| 1409 | $this->requiresFetchAfterChange = \true; |
| 1410 | } |
| 1411 | } |
| 1412 | public function setVersionField($versionField) |
| 1413 | { |
| 1414 | $this->versionField = $versionField; |
| 1415 | } |
| 1416 | public function markReadOnly() |
| 1417 | { |
| 1418 | $this->isReadOnly = \true; |
| 1419 | } |
| 1420 | public function getFieldNames() |
| 1421 | { |
| 1422 | return array_keys($this->fieldMappings); |
| 1423 | } |
| 1424 | public function getAssociationNames() |
| 1425 | { |
| 1426 | return array_keys($this->associationMappings); |
| 1427 | } |
| 1428 | public function getAssociationTargetClass($assocName) |
| 1429 | { |
| 1430 | if (!isset($this->associationMappings[$assocName])) { |
| 1431 | throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association."); |
| 1432 | } |
| 1433 | return $this->associationMappings[$assocName]['targetEntity']; |
| 1434 | } |
| 1435 | public function getName() |
| 1436 | { |
| 1437 | return $this->name; |
| 1438 | } |
| 1439 | public function getQuotedIdentifierColumnNames($platform) |
| 1440 | { |
| 1441 | $quotedColumnNames = []; |
| 1442 | foreach ($this->identifier as $idProperty) { |
| 1443 | if (isset($this->fieldMappings[$idProperty])) { |
| 1444 | $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName']) : $this->fieldMappings[$idProperty]['columnName']; |
| 1445 | continue; |
| 1446 | } |
| 1447 | // Association defined as Id field |
| 1448 | $joinColumns = $this->associationMappings[$idProperty]['joinColumns']; |
| 1449 | $assocQuotedColumnNames = array_map(static function ($joinColumn) use($platform) { |
| 1450 | return isset($joinColumn['quoted']) ? $platform->quoteIdentifier($joinColumn['name']) : $joinColumn['name']; |
| 1451 | }, $joinColumns); |
| 1452 | $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames); |
| 1453 | } |
| 1454 | return $quotedColumnNames; |
| 1455 | } |
| 1456 | public function getQuotedColumnName($field, $platform) |
| 1457 | { |
| 1458 | return isset($this->fieldMappings[$field]['quoted']) ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : $this->fieldMappings[$field]['columnName']; |
| 1459 | } |
| 1460 | public function getQuotedTableName($platform) |
| 1461 | { |
| 1462 | return isset($this->table['quoted']) ? $platform->quoteIdentifier($this->table['name']) : $this->table['name']; |
| 1463 | } |
| 1464 | public function getQuotedJoinTableName(array $assoc, $platform) |
| 1465 | { |
| 1466 | return isset($assoc['joinTable']['quoted']) ? $platform->quoteIdentifier($assoc['joinTable']['name']) : $assoc['joinTable']['name']; |
| 1467 | } |
| 1468 | public function isAssociationInverseSide($fieldName) |
| 1469 | { |
| 1470 | return isset($this->associationMappings[$fieldName]) && !$this->associationMappings[$fieldName]['isOwningSide']; |
| 1471 | } |
| 1472 | public function getAssociationMappedByTargetField($fieldName) |
| 1473 | { |
| 1474 | if (!$this->isAssociationInverseSide($fieldName)) { |
| 1475 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/11309', 'Calling %s with owning side field %s is deprecated and will no longer be supported in Doctrine ORM 3.0. Call %s::isAssociationInverseSide() to check first.', __METHOD__, $fieldName, self::class); |
| 1476 | } |
| 1477 | return $this->associationMappings[$fieldName]['mappedBy']; |
| 1478 | } |
| 1479 | public function getAssociationsByTargetClass($targetClass) |
| 1480 | { |
| 1481 | $relations = []; |
| 1482 | foreach ($this->associationMappings as $mapping) { |
| 1483 | if ($mapping['targetEntity'] === $targetClass) { |
| 1484 | $relations[$mapping['fieldName']] = $mapping; |
| 1485 | } |
| 1486 | } |
| 1487 | return $relations; |
| 1488 | } |
| 1489 | public function fullyQualifiedClassName($className) |
| 1490 | { |
| 1491 | if (empty($className)) { |
| 1492 | return $className; |
| 1493 | } |
| 1494 | if (!str_contains($className, '\\') && $this->namespace) { |
| 1495 | return $this->namespace . '\\' . $className; |
| 1496 | } |
| 1497 | return $className; |
| 1498 | } |
| 1499 | public function getMetadataValue($name) |
| 1500 | { |
| 1501 | if (isset($this->{$name})) { |
| 1502 | return $this->{$name}; |
| 1503 | } |
| 1504 | return null; |
| 1505 | } |
| 1506 | public function mapEmbedded(array $mapping) |
| 1507 | { |
| 1508 | $this->assertFieldNotMapped($mapping['fieldName']); |
| 1509 | if (!isset($mapping['class']) && $this->isTypedProperty($mapping['fieldName'])) { |
| 1510 | $type = $this->reflClass->getProperty($mapping['fieldName'])->getType(); |
| 1511 | if ($type instanceof ReflectionNamedType) { |
| 1512 | $mapping['class'] = $type->getName(); |
| 1513 | } |
| 1514 | } |
| 1515 | if (!(isset($mapping['class']) && $mapping['class'])) { |
| 1516 | throw MappingException::missingEmbeddedClass($mapping['fieldName']); |
| 1517 | } |
| 1518 | $fqcn = $this->fullyQualifiedClassName($mapping['class']); |
| 1519 | assert($fqcn !== null); |
| 1520 | $this->embeddedClasses[$mapping['fieldName']] = ['class' => $fqcn, 'columnPrefix' => $mapping['columnPrefix'] ?? null, 'declaredField' => $mapping['declaredField'] ?? null, 'originalField' => $mapping['originalField'] ?? null]; |
| 1521 | } |
| 1522 | public function inlineEmbeddable($property, ClassMetadataInfo $embeddable) |
| 1523 | { |
| 1524 | foreach ($embeddable->fieldMappings as $fieldMapping) { |
| 1525 | $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->name; |
| 1526 | $fieldMapping['declaredField'] = isset($fieldMapping['declaredField']) ? $property . '.' . $fieldMapping['declaredField'] : $property; |
| 1527 | $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldMapping['fieldName']; |
| 1528 | $fieldMapping['fieldName'] = $property . '.' . $fieldMapping['fieldName']; |
| 1529 | if (!empty($this->embeddedClasses[$property]['columnPrefix'])) { |
| 1530 | $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName']; |
| 1531 | } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== \false) { |
| 1532 | $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName($property, $fieldMapping['columnName'], $this->reflClass->name, $embeddable->reflClass->name); |
| 1533 | } |
| 1534 | $this->mapField($fieldMapping); |
| 1535 | } |
| 1536 | } |
| 1537 | private function assertFieldNotMapped(string $fieldName) : void |
| 1538 | { |
| 1539 | if (isset($this->fieldMappings[$fieldName]) || isset($this->associationMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName])) { |
| 1540 | throw MappingException::duplicateFieldMapping($this->name, $fieldName); |
| 1541 | } |
| 1542 | } |
| 1543 | public function getSequenceName(AbstractPlatform $platform) |
| 1544 | { |
| 1545 | $sequencePrefix = $this->getSequencePrefix($platform); |
| 1546 | $columnName = $this->getSingleIdentifierColumnName(); |
| 1547 | return $sequencePrefix . '_' . $columnName . '_seq'; |
| 1548 | } |
| 1549 | public function getSequencePrefix(AbstractPlatform $platform) |
| 1550 | { |
| 1551 | $tableName = $this->getTableName(); |
| 1552 | $sequencePrefix = $tableName; |
| 1553 | // Prepend the schema name to the table name if there is one |
| 1554 | $schemaName = $this->getSchemaName(); |
| 1555 | if ($schemaName) { |
| 1556 | $sequencePrefix = $schemaName . '.' . $tableName; |
| 1557 | // @phpstan-ignore method.deprecated |
| 1558 | if (!$platform->supportsSchemas() && $platform->canEmulateSchemas()) { |
| 1559 | $sequencePrefix = $schemaName . '__' . $tableName; |
| 1560 | } |
| 1561 | } |
| 1562 | return $sequencePrefix; |
| 1563 | } |
| 1564 | private function assertMappingOrderBy(array $mapping) : void |
| 1565 | { |
| 1566 | if (isset($mapping['orderBy']) && !is_array($mapping['orderBy'])) { |
| 1567 | throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy'])); |
| 1568 | } |
| 1569 | } |
| 1570 | private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field) : ?ReflectionProperty |
| 1571 | { |
| 1572 | $reflectionProperty = $reflService->getAccessibleProperty($class, $field); |
| 1573 | if ($reflectionProperty !== null && PHP_VERSION_ID >= 80100 && $reflectionProperty->isReadOnly()) { |
| 1574 | $declaringClass = $reflectionProperty->class; |
| 1575 | if ($declaringClass !== $class) { |
| 1576 | $reflectionProperty = $reflService->getAccessibleProperty($declaringClass, $field); |
| 1577 | } |
| 1578 | if ($reflectionProperty !== null) { |
| 1579 | $reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty); |
| 1580 | } |
| 1581 | } |
| 1582 | if (PHP_VERSION_ID >= 80400 && $reflectionProperty !== null && count($reflectionProperty->getHooks()) > 0) { |
| 1583 | throw new LogicException('Doctrine ORM does not support property hooks in this version. Check https://github.com/doctrine/orm/issues/11624 for details of versions that support property hooks.'); |
| 1584 | } |
| 1585 | return $reflectionProperty; |
| 1586 | } |
| 1587 | } |
| 1588 |