From cb78c4563d0e177042e52f6f869b31e6deb05c2f Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Fri, 5 Dec 2025 21:12:13 +0800 Subject: [PATCH 1/2] Drop deprecated features --- src/Configuration.php | 64 --------- src/Mapping/Annotations/EmbeddedDocument.php | 8 -- src/Mapping/Annotations/File.php | 10 +- src/Mapping/ClassMetadata.php | 80 +---------- src/Mapping/ClassMetadataFactory.php | 11 -- src/Mapping/Driver/AttributeDriver.php | 16 --- src/Mapping/LegacyReflectionFields.php | 136 ------------------ src/Mapping/MappingException.php | 5 + .../DefaultPersistentCollectionGenerator.php | 3 - .../PersistentCollectionTrait.php | 16 --- src/Persisters/DocumentPersister.php | 15 -- src/UnitOfWork.php | 46 +----- tests/Documents/ProfileNotify.php | 1 - .../Functional/CustomCollectionsTest.php | 5 +- .../Functional/DocumentPersisterTest.php | 22 --- tests/Tests/Mapping/ClassMetadataTest.php | 3 - .../Mapping/LegacyReflectionFieldsTest.php | 92 ------------ tests/Tests/UnitOfWorkTest.php | 122 ---------------- 18 files changed, 16 insertions(+), 639 deletions(-) delete mode 100644 src/Mapping/LegacyReflectionFields.php delete mode 100644 tests/Tests/Mapping/LegacyReflectionFieldsTest.php diff --git a/src/Configuration.php b/src/Configuration.php index ecba637587..99eb906146 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -28,11 +28,8 @@ use function array_diff_key; use function array_intersect_key; -use function array_key_exists; use function interface_exists; use function is_string; -use function trigger_deprecation; -use function trim; /** * Configuration class for the DocumentManager. When setting up your DocumentManager @@ -88,7 +85,6 @@ class Configuration * defaultDocumentRepositoryClassName?: class-string>, * defaultGridFSRepositoryClassName?: class-string>, * defaultDB?: string, - * documentNamespaces?: array, * filters?: array @@ -156,55 +152,6 @@ public function getClientEncryptionOptions(): array ]); } - /** - * Adds a namespace under a certain alias. - */ - public function addDocumentNamespace(string $alias, string $namespace): void - { - $this->attributes['documentNamespaces'][$alias] = $namespace; - } - - /** - * Resolves a registered namespace alias to the full namespace. - * - * @throws MongoDBException - */ - public function getDocumentNamespace(string $documentNamespaceAlias): string - { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.3', - 'Document short namespace aliases such as "%s" are deprecated, use ::class constant instead.', - $documentNamespaceAlias, - ); - - if (! isset($this->attributes['documentNamespaces'][$documentNamespaceAlias])) { - throw MongoDBException::unknownDocumentNamespace($documentNamespaceAlias); - } - - return trim($this->attributes['documentNamespaces'][$documentNamespaceAlias], '\\'); - } - - /** - * Retrieves the list of registered document namespace aliases. - * - * @return array - */ - public function getDocumentNamespaces(): array - { - return $this->attributes['documentNamespaces']; - } - - /** - * Set the document alias map - * - * @param array $documentNamespaces - */ - public function setDocumentNamespaces(array $documentNamespaces): void - { - $this->attributes['documentNamespaces'] = $documentNamespaces; - } - /** * Sets the cache driver implementation that is used for metadata caching. * @@ -374,17 +321,6 @@ public function getDefaultCommitOptions(): array /** @phpstan-param CommitOptions $defaultCommitOptions */ public function setDefaultCommitOptions(array $defaultCommitOptions): void { - foreach (UnitOfWork::DEPRECATED_WRITE_OPTIONS as $deprecatedOption) { - if (array_key_exists($deprecatedOption, $defaultCommitOptions)) { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.6', - 'The "%s" commit option used in the configuration is deprecated.', - $deprecatedOption, - ); - } - } - $this->attributes['defaultCommitOptions'] = $defaultCommitOptions; } diff --git a/src/Mapping/Annotations/EmbeddedDocument.php b/src/Mapping/Annotations/EmbeddedDocument.php index 11488ebfcf..a57f605c85 100644 --- a/src/Mapping/Annotations/EmbeddedDocument.php +++ b/src/Mapping/Annotations/EmbeddedDocument.php @@ -12,12 +12,4 @@ #[Attribute(Attribute::TARGET_CLASS)] final class EmbeddedDocument extends AbstractDocument { - /** @var Index[] */ - public $indexes; - - /** @param Index[] $indexes */ - public function __construct(array $indexes = []) - { - $this->indexes = $indexes; - } } diff --git a/src/Mapping/Annotations/File.php b/src/Mapping/Annotations/File.php index 3f39626eb4..4560f73c46 100644 --- a/src/Mapping/Annotations/File.php +++ b/src/Mapping/Annotations/File.php @@ -21,9 +21,6 @@ final class File extends AbstractDocument /** @var string|null */ public $repositoryClass; - /** @var Index[] */ - public $indexes; - /** @var bool bool */ public $readOnly; @@ -33,15 +30,11 @@ final class File extends AbstractDocument /** @var int|null */ public $chunkSizeBytes; - /** - * @param Index[] $indexes - * @param string|int|null $writeConcern - */ + /** @param string|int|null $writeConcern */ public function __construct( ?string $db = null, ?string $bucketName = null, ?string $repositoryClass = null, - array $indexes = [], bool $readOnly = false, ?string $shardKey = null, public $writeConcern = null, @@ -50,7 +43,6 @@ public function __construct( $this->db = $db; $this->bucketName = $bucketName; $this->repositoryClass = $repositoryClass; - $this->indexes = $indexes; $this->readOnly = $readOnly; $this->shardKey = $shardKey; $this->chunkSizeBytes = $chunkSizeBytes; diff --git a/src/Mapping/ClassMetadata.php b/src/Mapping/ClassMetadata.php index 88ac7751c1..15fb394cb5 100644 --- a/src/Mapping/ClassMetadata.php +++ b/src/Mapping/ClassMetadata.php @@ -23,8 +23,6 @@ use Doctrine\ODM\MongoDB\Types\Versionable; use Doctrine\ODM\MongoDB\Utility\CollectionHelper; use Doctrine\Persistence\Mapping\ClassMetadata as BaseClassMetadata; -use Doctrine\Persistence\Mapping\ReflectionService; -use Doctrine\Persistence\Mapping\RuntimeReflectionService; use InvalidArgumentException; use LogicException; use MongoDB\BSON\Decimal128; @@ -33,7 +31,6 @@ use ReflectionClass; use ReflectionEnum; use ReflectionNamedType; -use ReflectionProperty; use Symfony\Component\Uid\UuidV1; use Symfony\Component\Uid\UuidV4; use Symfony\Component\Uid\UuidV7; @@ -59,7 +56,6 @@ use function sprintf; use function strtolower; use function strtoupper; -use function trigger_deprecation; /** * A ClassMetadata instance holds all the object-document mapping metadata @@ -436,15 +432,6 @@ final class ClassMetadata implements BaseClassMetadata */ public const CHANGETRACKING_DEFERRED_EXPLICIT = 2; - /** - * NOTIFY means that Doctrine relies on the entities sending out notifications - * when their properties change. Such entity classes must implement - * the NotifyPropertyChanged interface. - * - * @deprecated - */ - public const CHANGETRACKING_NOTIFY = 3; - /** * SET means that fields will be written to the database using a $set operator */ @@ -609,15 +596,6 @@ final class ClassMetadata implements BaseClassMetadata */ public private(set) array $subClasses = []; - /** - * The ReflectionProperty instances of the mapped class. - * - * @deprecated Since 2.13, use $propertyAccessors instead. - * - * @var LegacyReflectionFields|array - */ - public private(set) LegacyReflectionFields|array $reflFields = []; - /** @var array */ public private(set) array $propertyAccessors = []; @@ -802,8 +780,6 @@ final class ClassMetadata implements BaseClassMetadata private InstantiatorInterface $instantiator; - private ReflectionService $reflectionService; - /** @var class-string|null */ private ?string $rootClass; @@ -815,11 +791,9 @@ final class ClassMetadata implements BaseClassMetadata */ public function __construct(string $documentName) { - $this->name = $documentName; - $this->rootDocumentName = $documentName; - $this->reflectionService = new RuntimeReflectionService(); - $this->reflClass = new ReflectionClass($documentName); - $this->reflFields = new LegacyReflectionFields($this, $this->reflectionService); + $this->name = $documentName; + $this->rootDocumentName = $documentName; + $this->reflClass = new ReflectionClass($documentName); $this->setCollection($this->reflClass->getShortName()); $this->instantiator = new Instantiator(); } @@ -1424,29 +1398,6 @@ public function isChangeTrackingDeferredImplicit(): bool return $this->changeTrackingPolicy === self::CHANGETRACKING_DEFERRED_IMPLICIT; } - /** - * Whether the change tracking policy of this class is "notify". - * - * @deprecated This method was deprecated in doctrine/mongodb-odm 2.4. Please use DEFERRED_EXPLICIT tracking - * policy and isChangeTrackingDeferredImplicit method to detect it. - */ - public function isChangeTrackingNotify(): bool - { - return $this->changeTrackingPolicy === self::CHANGETRACKING_NOTIFY; - } - - /** - * Gets the ReflectionProperties of the mapped class. - * - * @deprecated Since 2.13, use getPropertyAccessors() instead. - * - * @return array|LegacyReflectionFields - */ - public function getReflectionProperties(): array|LegacyReflectionFields - { - return $this->reflFields; - } - /** * Gets the ReflectionProperties of the mapped class. * @@ -1457,16 +1408,6 @@ public function getPropertyAccessors(): array return $this->propertyAccessors; } - /** - * Gets a ReflectionProperty for a specific field of the mapped class. - * - * @deprecated Since 2.13, use getPropertyAccessor() instead. - */ - public function getReflectionProperty(string $name): ReflectionProperty - { - return $this->reflFields[$name]; - } - public function getPropertyAccessor(string $name): PropertyAccessor|null { return $this->propertyAccessors[$name] ?? null; @@ -2407,13 +2348,7 @@ public function mapField(array $mapping): array } if (isset($mapping['targetDocument']) && isset($mapping['discriminatorMap'])) { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.2', - 'Mapping both "targetDocument" and "discriminatorMap" on field "%s" in class "%s" is deprecated. Only one of them can be used at a time', - $mapping['fieldName'], - $this->name, - ); + throw MappingException::targetDocumentCanNotBeCombinedWithDiscriminatorMap($this->name, $mapping['fieldName']); } if (isset($mapping['reference']) && $mapping['type'] === self::ONE) { @@ -2535,7 +2470,6 @@ public function mapField(array $mapping): array * * Parts that are also NOT serialized because they cannot be properly unserialized: * - reflClass (ReflectionClass) - * - reflFields (ReflectionProperty array) * - propertyAccessors (ReflectionProperty array) * * @return array The names of all the fields that should be serialized. @@ -2643,10 +2577,8 @@ public function __sleep(): array public function __wakeup(): void { // Restore ReflectionClass and properties - $this->reflClass = new ReflectionClass($this->name); - $this->instantiator = new Instantiator(); - $this->reflectionService = new RuntimeReflectionService(); - $this->reflFields = new LegacyReflectionFields($this, $this->reflectionService); + $this->reflClass = new ReflectionClass($this->name); + $this->instantiator = new Instantiator(); foreach ($this->fieldMappings as $field => $mapping) { $accessor = PropertyAccessorFactory::createPropertyAccessor($mapping['declared'] ?? $this->name, $field); diff --git a/src/Mapping/ClassMetadataFactory.php b/src/Mapping/ClassMetadataFactory.php index 91e901bf69..64d31a5e35 100644 --- a/src/Mapping/ClassMetadataFactory.php +++ b/src/Mapping/ClassMetadataFactory.php @@ -27,7 +27,6 @@ use function get_class_methods; use function in_array; use function interface_exists; -use function trigger_deprecation; use function ucfirst; /** @@ -197,16 +196,6 @@ protected function doLoadMetadata(ClassMetadataInterface $class, ?ClassMetadataI Events::loadClassMetadata, new LoadClassMetadataEventArgs($class, $this->dm), ); - - // phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed - if ($class->isChangeTrackingNotify()) { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.4', - 'NOTIFY tracking policy used in class "%s" is deprecated. Please use DEFERRED_EXPLICIT instead.', - $class->name, - ); - } } /** diff --git a/src/Mapping/Driver/AttributeDriver.php b/src/Mapping/Driver/AttributeDriver.php index 79485ef003..0096e49e5c 100644 --- a/src/Mapping/Driver/AttributeDriver.php +++ b/src/Mapping/Driver/AttributeDriver.php @@ -26,9 +26,7 @@ use function assert; use function class_exists; use function constant; -use function count; use function is_array; -use function trigger_deprecation; /** * The AttributeDriver reads the mapping metadata from attributes. @@ -189,20 +187,6 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $metadata->setWriteConcern($documentAttribute->writeConcern); } - if (isset($documentAttribute->indexes) && count($documentAttribute->indexes)) { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.2', - 'The "indexes" parameter in the "%s" attribute for class "%s" is deprecated. Specify all "@Index" and "@UniqueIndex" attributes on the class.', - $documentAttribute::class, - $className, - ); - - foreach ($documentAttribute->indexes as $index) { - $this->addIndex($metadata, $index); - } - } - if (! empty($documentAttribute->readOnly)) { $metadata->markReadOnly(); } diff --git a/src/Mapping/LegacyReflectionFields.php b/src/Mapping/LegacyReflectionFields.php deleted file mode 100644 index 138f6fa685..0000000000 --- a/src/Mapping/LegacyReflectionFields.php +++ /dev/null @@ -1,136 +0,0 @@ - - * @template-implements IteratorAggregate - */ -class LegacyReflectionFields implements ArrayAccess, IteratorAggregate, Countable -{ - /** @var array */ - private array $reflFields = []; - - public function __construct(private ClassMetadata $classMetadata, private ReflectionService $reflectionService) - { - } - - /** @param string $offset */ - public function offsetExists($offset): bool // phpcs:ignore - { - trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ODM 3.0.'); - - return isset($this->classMetadata->propertyAccessors[$offset]); - } - - /** - * @param string $field - * - * @psalm-suppress LessSpecificImplementedReturnType - */ - public function offsetGet($field): mixed // phpcs:ignore - { - if (isset($this->reflFields[$field])) { - return $this->reflFields[$field]; - } - - trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ODM 3.0.'); - - if (! isset($this->classMetadata->propertyAccessors[$field])) { - throw new OutOfBoundsException('Unknown field: ' . $this->classMetadata->name . ' ::$' . $field); - } - - $className = $this->classMetadata->fieldMappings[$field]['inherited'] - ?? $this->classMetadata->fieldMappings[$field]['declared'] - ?? $this->classMetadata->associationMappings[$field]['declared'] - ?? $this->classMetadata->name; - - $this->reflFields[$field] = $this->getAccessibleProperty($className, $field); - - if (isset($this->classMetadata->fieldMappings[$field])) { - if ($this->classMetadata->fieldMappings[$field]['enumType'] ?? null) { - $this->reflFields[$field] = new EnumReflectionProperty( - $this->reflFields[$field], - $this->classMetadata->fieldMappings[$field]['enumType'], - ); - } - } - - return $this->reflFields[$field]; - } - - /** - * @param string $offset - * @param ReflectionProperty $value - */ - public function offsetSet($offset, $value): void // phpcs:ignore - { - $this->reflFields[$offset] = $value; - } - - /** @param string $offset */ - public function offsetUnset($offset): void // phpcs:ignore - { - unset($this->reflFields[$offset]); - } - - /** @psalm-param class-string $class */ - private function getAccessibleProperty(string $class, string $field): ReflectionProperty - { - $reflectionProperty = $this->reflectionService->getAccessibleProperty($class, $field); - - assert($reflectionProperty !== null); - - if ($reflectionProperty->isReadOnly()) { - $declaringClass = $reflectionProperty->class; - if ($declaringClass !== $class) { - $reflectionProperty = $this->reflectionService->getAccessibleProperty($declaringClass, $field); - - assert($reflectionProperty !== null); - } - - $reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty); - } - - return $reflectionProperty; - } - - /** @return Generator */ - public function getIterator(): Traversable - { - trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ODM 3.0.'); - - $keys = array_keys($this->classMetadata->propertyAccessors); - - foreach ($keys as $key) { - yield $key => $this->offsetGet($key); - } - } - - public function count(): int - { - trigger_deprecation('doctrine/mongodb-odm', '2.14', 'Access to ClassMetadata::$reflFields is deprecated and will be removed in Doctrine ODM 3.0.'); - - return count($this->classMetadata->propertyAccessors); - } -} diff --git a/src/Mapping/MappingException.php b/src/Mapping/MappingException.php index d91b95e5b0..0ae89dd01f 100644 --- a/src/Mapping/MappingException.php +++ b/src/Mapping/MappingException.php @@ -232,6 +232,11 @@ public static function repositoryMethodCanNotBeCombinedWithSkipLimitAndSort(stri return new self(sprintf("'repositoryMethod' used on '%s' in class '%s' can not be combined with skip, limit or sort.", $fieldName, $className)); } + public static function targetDocumentCanNotBeCombinedWithDiscriminatorMap(string $className, string $fieldName): self + { + return new self(sprintf("'targetDocument' used on '%s' in class '%s' can not be combined with 'discriminatorMap'.", $fieldName, $className)); + } + public static function xmlMappingFileInvalid(string $filename, string $errorDetails): self { return new self(sprintf("The mapping file %s is invalid: \n%s", $filename, $errorDetails)); diff --git a/src/PersistentCollection/DefaultPersistentCollectionGenerator.php b/src/PersistentCollection/DefaultPersistentCollectionGenerator.php index 18d77b217f..d48d991259 100644 --- a/src/PersistentCollection/DefaultPersistentCollectionGenerator.php +++ b/src/PersistentCollection/DefaultPersistentCollectionGenerator.php @@ -196,9 +196,6 @@ private function generateMethod(ReflectionMethod $method): string public function {$method->name}($parametersString){$this->getMethodReturnType($method)} { \$this->initialize(); - if (\$this->needsSchedulingForSynchronization()) { - \$this->changed(); - } {$return}\$this->coll->{$method->name}($callParamsString); } diff --git a/src/PersistentCollection/PersistentCollectionTrait.php b/src/PersistentCollection/PersistentCollectionTrait.php index ec003ae1b3..12ed023d87 100644 --- a/src/PersistentCollection/PersistentCollectionTrait.php +++ b/src/PersistentCollection/PersistentCollectionTrait.php @@ -20,7 +20,6 @@ use function array_udiff_assoc; use function array_values; use function count; -use function get_class; use function is_object; use function method_exists; @@ -174,12 +173,6 @@ private function changed(): void } $this->isDirty = true; - - if (! $this->needsSchedulingForSynchronization() || $this->owner === null) { - return; - } - - $this->uow->scheduleForSynchronization($this->owner); } public function isDirty(): bool @@ -739,15 +732,6 @@ private function isOrphanRemovalEnabled(): bool return isset($this->mapping['reference']) && $this->mapping['isOwningSide'] && $this->mapping['orphanRemoval']; } - /** - * Checks whether collection owner needs to be scheduled for dirty change in case the collection is modified. - */ - private function needsSchedulingForSynchronization(): bool - { - return $this->owner && isset($this->dm) && ! empty($this->mapping['isOwningSide']) - && $this->dm->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify(); - } - /** * @phpstan-param Closure(TKey, T):bool $p * diff --git a/src/Persisters/DocumentPersister.php b/src/Persisters/DocumentPersister.php index 7022365946..a356a7b9e4 100644 --- a/src/Persisters/DocumentPersister.php +++ b/src/Persisters/DocumentPersister.php @@ -42,7 +42,6 @@ use function array_combine; use function array_fill; use function array_intersect_key; -use function array_key_exists; use function array_keys; use function array_map; use function array_merge; @@ -64,7 +63,6 @@ use function str_contains; use function strpos; use function strtolower; -use function trigger_deprecation; /** * The DocumentPersister is responsible for persisting documents. @@ -74,10 +72,7 @@ * @template T of object = object * * @phpstan-type CommitOptions array{ - * fsync?: bool, - * safe?: int, * session?: ?Session, - * w?: int, * withTransaction?: bool, * writeConcern?: WriteConcern * } @@ -1549,16 +1544,6 @@ private function getWriteOptions(array $options = []): array } $writeOptions = array_merge($defaultOptions, $documentOptions, $options); - if (array_key_exists('w', $writeOptions)) { - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.2', - 'The "w" option as commit option is deprecated, please pass "%s" object in "writeConcern" option.', - WriteConcern::class, - ); - $writeOptions['writeConcern'] = new WriteConcern($writeOptions['w']); - unset($writeOptions['w']); - } return $this->isInTransaction($options) ? $this->uow->stripTransactionOptions($writeOptions) diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index 9ca2b86a13..b66cea1f0b 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -34,7 +34,6 @@ use function array_diff_key; use function array_filter; use function array_intersect_key; -use function array_key_exists; use function array_merge; use function assert; use function call_user_func; @@ -48,7 +47,6 @@ use function serialize; use function spl_object_id; use function sprintf; -use function trigger_deprecation; /** * The UnitOfWork is responsible for tracking changes to objects during an @@ -63,9 +61,6 @@ * } * @phpstan-type Hints array * @phpstan-type CommitOptions array{ - * fsync?: bool, - * safe?: int, - * w?: int, * withTransaction?: bool, * writeConcern?: WriteConcern * } @@ -96,9 +91,7 @@ final class UnitOfWork implements PropertyChangedListener */ public const int STATE_REMOVED = 4; - /** @internal */ - public const array DEPRECATED_WRITE_OPTIONS = ['fsync', 'safe', 'w']; - private const array TRANSACTION_OPTIONS = [ + private const array TRANSACTION_OPTIONS = [ 'maxCommitTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, @@ -410,19 +403,6 @@ public function setDocumentPersister(string $documentName, Persisters\DocumentPe */ public function commit(array $options = []): void { - foreach (self::DEPRECATED_WRITE_OPTIONS as $deprecatedOption) { - if (! array_key_exists($deprecatedOption, $options)) { - continue; - } - - trigger_deprecation( - 'doctrine/mongodb-odm', - '2.6', - 'The "%s" commit option is deprecated.', - $deprecatedOption, - ); - } - // Raise preFlush $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->dm)); @@ -732,13 +712,8 @@ private function computeOrRecomputeChangeSet(ClassMetadata $class, object $docum // Document is "fully" MANAGED: it was already fully persisted before // and we have a copy of the original data - $originalData = $this->originalDocumentData[$oid]; - $isChangeTrackingNotify = $class->isChangeTrackingNotify(); - if ($isChangeTrackingNotify && ! $recompute && isset($this->documentChangeSets[$oid])) { - $changeSet = $this->documentChangeSets[$oid]; - } else { - $changeSet = []; - } + $originalData = $this->originalDocumentData[$oid]; + $changeSet = []; $gridFSMetadataProperty = null; @@ -793,10 +768,6 @@ private function computeOrRecomputeChangeSet(ClassMetadata $class, object $docum continue; } - if ($isChangeTrackingNotify) { - continue; - } - // ignore inverse side of reference relationship if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['isInverseSide']) { continue; @@ -2015,21 +1986,10 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged if (! $managedCol->isEmpty() && $managedCol !== $mergeCol) { $managedCol->unwrap()->clear(); $managedCol->setDirty(true); - - if ($assoc2['isOwningSide'] && $class->isChangeTrackingNotify()) { - $this->scheduleForSynchronization($managedCopy); - } } } } } - - if (! $class->isChangeTrackingNotify()) { - continue; - } - - // Just treat all properties as changed, there is no other choice. - $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); } if ($class->isChangeTrackingDeferredExplicit()) { diff --git a/tests/Documents/ProfileNotify.php b/tests/Documents/ProfileNotify.php index 1453768d2e..e3a006f6df 100644 --- a/tests/Documents/ProfileNotify.php +++ b/tests/Documents/ProfileNotify.php @@ -10,7 +10,6 @@ use Doctrine\Persistence\PropertyChangedListener; #[ODM\Document] -#[ODM\ChangeTrackingPolicy('NOTIFY')] class ProfileNotify implements NotifyPropertyChanged { /** @var string|null */ diff --git a/tests/Tests/Functional/CustomCollectionsTest.php b/tests/Tests/Functional/CustomCollectionsTest.php index d821746573..6eb4d74dc5 100644 --- a/tests/Tests/Functional/CustomCollectionsTest.php +++ b/tests/Tests/Functional/CustomCollectionsTest.php @@ -16,7 +16,6 @@ use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; use Documents\File; use Documents\ProfileNotify; -use PHPUnit\Framework\Attributes\IgnoreDeprecations; use stdClass; use function assert; @@ -149,9 +148,7 @@ public function testModifyingCollectionByCustomMethod(): void self::assertEquals($e1, $d->coll[1]); } - /** @see ClassMetadata::CHANGETRACKING_NOTIFY */ - #[IgnoreDeprecations] - public function testModifyingCollectionInChangeTrackingNotifyDocument(): void + public function testModifyingCollection(): void { $repository = $this->dm->getRepository(File::class); assert($repository instanceof GridFSRepository); diff --git a/tests/Tests/Functional/DocumentPersisterTest.php b/tests/Tests/Functional/DocumentPersisterTest.php index b082f437f5..339585aab4 100644 --- a/tests/Tests/Functional/DocumentPersisterTest.php +++ b/tests/Tests/Functional/DocumentPersisterTest.php @@ -802,28 +802,6 @@ public function testDefaultWriteConcernIsIgnoredInTransaction(): void $this->dm->flush(); } - public function testDefaultWriteConcernIsRespectedBackwardCompatibility(): void - { - $this->skipTestIfTransactionalFlushEnabled(); - - $class = DocumentPersisterTestDocument::class; - $documentPersister = $this->uow->getDocumentPersister($class); - - $collection = $this->createMock(Collection::class); - $collection->expects($this->once()) - ->method('insertMany') - ->with($this->isType('array'), $this->equalTo(['writeConcern' => new WriteConcern(0)])); - - $reflectionProperty = new ReflectionProperty($documentPersister, 'collection'); - $reflectionProperty->setValue($documentPersister, $collection); - - $this->dm->getConfiguration()->setDefaultCommitOptions(['w' => 0]); - - $testDocument = new $class(); - $this->dm->persist($testDocument); - $this->dm->flush(); - } - public function testVersionIncrementOnUpdateSuccess(): void { $testDocument = new DocumentPersisterTestDocumentWithVersion(); diff --git a/tests/Tests/Mapping/ClassMetadataTest.php b/tests/Tests/Mapping/ClassMetadataTest.php index 3ce4a49f5c..4468f208a5 100644 --- a/tests/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Tests/Mapping/ClassMetadataTest.php @@ -8,7 +8,6 @@ use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; -use Doctrine\ODM\MongoDB\Mapping\LegacyReflectionFields; use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Mapping\PropertyAccessors\EnumPropertyAccessor; use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; @@ -57,7 +56,6 @@ public function testClassMetadataInstanceSerialization(): void $cm = new ClassMetadata(CmsUser::class); // Test initial state - self::assertInstanceOf(LegacyReflectionFields::class, $cm->getReflectionProperties()); self::assertEmpty($cm->getPropertyAccessors()); self::assertInstanceOf(ReflectionClass::class, $cm->reflClass); self::assertEquals(CmsUser::class, $cm->name); @@ -93,7 +91,6 @@ public function testClassMetadataInstanceSerialization(): void $cm = unserialize($serialized); // Check state - self::assertInstanceOf(LegacyReflectionFields::class, $cm->getReflectionProperties()); self::assertNotEmpty($cm->getPropertyAccessors()); self::assertInstanceOf(ReflectionClass::class, $cm->reflClass); self::assertEquals(CmsUser::class, $cm->name); diff --git a/tests/Tests/Mapping/LegacyReflectionFieldsTest.php b/tests/Tests/Mapping/LegacyReflectionFieldsTest.php deleted file mode 100644 index 9d8c201f12..0000000000 --- a/tests/Tests/Mapping/LegacyReflectionFieldsTest.php +++ /dev/null @@ -1,92 +0,0 @@ -dm->getClassMetadata(User::class); - self::assertInstanceOf(LegacyReflectionFields::class, $class->reflFields); - - $user = new User(); - $user->setUsername('Jean'); - $user->setInheritedProperty('inherited'); - $address = new Address(); - $address->setCity('Paris'); - $user->setAddress($address); - - $this->dm->persist($user); - $this->dm->flush(); - $this->dm->clear(); - - $user = $this->dm->find(User::class, $user->getId()); - - // Accessing the field directly through reflection - self::assertEquals('Jean', $class->getReflectionProperty('username')->getValue($user)); - $class->getReflectionProperty('username')->setValue($user, 'Marie'); - self::assertEquals('Marie', $class->getReflectionProperty('username')->getValue($user)); - - // Accessing a private field 'inheritedProperty' of the parent class through reflection - self::assertEquals('inherited', $class->getReflectionProperty('inheritedProperty')->getValue($user)); - $class->getReflectionProperty('inheritedProperty')->setValue($user, 'changed'); - self::assertEquals('changed', $class->getReflectionProperty('inheritedProperty')->getValue($user)); - - // Accessing a field in a related document through reflection - self::assertEquals('Paris', $class->getReflectionProperty('address')->getValue($user)->getCity()); - $class->getReflectionProperty('address')->setValue($user, $newAddress = new Address()); - self::assertSame($newAddress, $class->getReflectionProperty('address')->getValue($user)); - - // ArrayAccess and Countable interfaces - self::assertCount(32, $class->reflFields); - self::assertArrayHasKey('username', $class->reflFields); - self::assertArrayNotHasKey('nonExistentField', $class->reflFields); - } - - public function testGetSetReadonly(): void - { - $class = $this->dm->getClassMetadata(ReadOnlyProperty::class); - self::assertInstanceOf(LegacyReflectionFields::class, $class->reflFields); - - $tag = new ReadOnlyProperty('Important'); - $this->dm->persist($tag); - $this->dm->flush(); - - $tag = $this->dm->find(ReadOnlyProperty::class, $tag->id); - - // Accessing the readonly property through reflection - self::assertEquals('Important', $class->getReflectionProperty('name')->getValue($tag)); - - self::expectException(LogicException::class); - self::expectExceptionMessage(sprintf('Attempting to change readonly property %s::$name', ReadOnlyProperty::class)); - $class->getReflectionProperty('name')->setValue($tag, 'Very Important'); - } -} - -#[Document] -class ReadOnlyProperty -{ - #[Id] - public string $id; - - public function __construct( - #[Field] - public readonly string $name, - ) { - } -} diff --git a/tests/Tests/UnitOfWorkTest.php b/tests/Tests/UnitOfWorkTest.php index 5f7209e4cd..31f505ecaa 100644 --- a/tests/Tests/UnitOfWorkTest.php +++ b/tests/Tests/UnitOfWorkTest.php @@ -6,16 +6,12 @@ use Closure; use DateTime; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\APM\CommandLogger; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\MongoDBException; use Doctrine\ODM\MongoDB\Tests\Mocks\ExceptionThrowingListenerMock; use Doctrine\ODM\MongoDB\Tests\Mocks\PreUpdateListenerMock; use Doctrine\ODM\MongoDB\UnitOfWork; -use Doctrine\Persistence\NotifyPropertyChanged; -use Doctrine\Persistence\PropertyChangedListener; use Documents\Address; use Documents\File; use Documents\FileWithoutMetadata; @@ -611,124 +607,6 @@ public function __construct(string $name) } } -#[ODM\Document] -#[ODM\ChangeTrackingPolicy('NOTIFY')] -class NotifyChangedDocument implements NotifyPropertyChanged -{ - /** @var PropertyChangedListener[] */ - private $_listeners = []; - - /** @var int|null */ - #[ODM\Id(type: 'int', strategy: 'none')] - private $id; - - /** @var string|null */ - #[ODM\Field(type: 'string')] - private $data; - - /** @var Collection */ - #[ODM\ReferenceMany(targetDocument: NotifyChangedRelatedItem::class)] - private $items; - - /** @var mixed */ - private $transient; // not persisted - - public function __construct() - { - $this->items = new ArrayCollection(); - } - - public function getId(): ?int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; - } - - public function getData(): ?string - { - return $this->data; - } - - public function setData(string $data): void - { - if ($data === $this->data) { - return; - } - - $this->onPropertyChanged('data', $this->data, $data); - $this->data = $data; - } - - /** @return Collection */ - public function getItems(): Collection - { - return $this->items; - } - - /** @param mixed $value */ - public function setTransient($value): void - { - if ($value === $this->transient) { - return; - } - - $this->onPropertyChanged('transient', $this->transient, $value); - $this->transient = $value; - } - - public function addPropertyChangedListener(PropertyChangedListener $listener): void - { - $this->_listeners[] = $listener; - } - - /** - * @param mixed $oldValue - * @param mixed $newValue - */ - protected function onPropertyChanged(string $propName, $oldValue, $newValue): void - { - foreach ($this->_listeners as $listener) { - $listener->propertyChanged($this, $propName, $oldValue, $newValue); - } - } -} - -#[ODM\Document] -class NotifyChangedRelatedItem -{ - /** @var int|null */ - #[ODM\Id(type: 'int', strategy: 'none')] - private $id; - - /** @var NotifyChangedDocument|null */ - #[ODM\ReferenceOne(targetDocument: NotifyChangedDocument::class)] - private $owner; - - public function getId(): ?int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; - } - - public function getOwner(): ?NotifyChangedDocument - { - return $this->owner; - } - - public function setOwner(NotifyChangedDocument $owner): void - { - $this->owner = $owner; - } -} - #[ODM\Document] class ArrayDocument { From 065cb083b39371ac7e5a141848997d362f1627e0 Mon Sep 17 00:00:00 2001 From: Ion Bazan Date: Fri, 5 Dec 2025 22:16:56 +0800 Subject: [PATCH 2/2] Remove array syntax in `setDiscriminatorField` --- src/Mapping/ClassMetadata.php | 14 +------------- .../Mapping/AbstractMappingDriverTestCase.php | 1 - 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Mapping/ClassMetadata.php b/src/Mapping/ClassMetadata.php index 15fb394cb5..d718ab7c57 100644 --- a/src/Mapping/ClassMetadata.php +++ b/src/Mapping/ClassMetadata.php @@ -1030,12 +1030,10 @@ public function setAlsoLoadMethods(array $methods): void * are only used to discern the hydration class and are not mapped to class * properties. * - * @param array{name?: string, fieldName?: string}|string|null $discriminatorField - * * @throws MappingException If the discriminator field conflicts with the * "name" attribute of a mapped field. */ - public function setDiscriminatorField(array|string|null $discriminatorField): void + public function setDiscriminatorField(?string $discriminatorField): void { if ($this->isFile) { throw MappingException::discriminatorNotAllowedForGridFS($this->name); @@ -1047,16 +1045,6 @@ public function setDiscriminatorField(array|string|null $discriminatorField): vo return; } - // @todo: deprecate, document and remove this: - // Handle array argument with name/fieldName keys for BC - if (is_array($discriminatorField)) { - if (isset($discriminatorField['name'])) { - $discriminatorField = $discriminatorField['name']; - } elseif (isset($discriminatorField['fieldName'])) { - $discriminatorField = $discriminatorField['fieldName']; - } - } - foreach ($this->fieldMappings as $fieldMapping) { if ($discriminatorField === $fieldMapping['name']) { throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField); diff --git a/tests/Tests/Mapping/AbstractMappingDriverTestCase.php b/tests/Tests/Mapping/AbstractMappingDriverTestCase.php index a01721675c..a4d75fb985 100644 --- a/tests/Tests/Mapping/AbstractMappingDriverTestCase.php +++ b/tests/Tests/Mapping/AbstractMappingDriverTestCase.php @@ -869,7 +869,6 @@ public static function loadMetadata(ClassMetadata $metadata): void $metadata->addLifecycleCallback('doStuffOnPrePersist', 'prePersist'); $metadata->addLifecycleCallback('doOtherStuffOnPrePersistToo', 'prePersist'); $metadata->addLifecycleCallback('doStuffOnPostPersist', 'postPersist'); - $metadata->setDiscriminatorField(['fieldName' => 'discr']); $metadata->setDiscriminatorMap(['default' => self::class]); $metadata->setDefaultDiscriminatorValue('default'); $metadata->mapField([