diff --git a/UPGRADE.md b/UPGRADE.md index a7d7824c10b..5b84d48a366 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,15 @@ # Upgrade to 3.0 +## BC BREAK: Remove ability to merge detached entities + +Merge semantics was a poor fit for the PHP "share-nothing" architecture. +In addition to that, merging caused multiple issues with data integrity +in the managed entity graph, which was constantly spawning more edge-case +bugs/scenarios. + +The method `UnitOfWork::merge()` has been removed. The method +`EntityManager::merge()` will throw an exception on each call. + ## BC BREAK: Removed ability to partially flush/commit entity manager and unit of work The following methods don't accept a single entity or an array of entities anymore: diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 4b6cf01d825..32e1df09c3a 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -4,13 +4,13 @@ namespace Doctrine\ORM; +use BadMethodCallException; use Doctrine\Common\EventManager; use Doctrine\Common\Util\ClassUtils; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\LockMode; -use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\Exception\EntityManagerClosed; use Doctrine\ORM\Exception\InvalidHydrationMode; use Doctrine\ORM\Exception\ManagerException; @@ -542,35 +542,15 @@ public function detach($entity): void } /** - * Merges the state of a detached entity into the persistence context - * of this EntityManager and returns the managed copy of the entity. - * The entity passed to merge will not become associated/managed with this EntityManager. + * Not supported. * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement + * @param object $object * - * @param object $entity The detached entity to merge into the persistence context. - * - * @return object The managed copy of the entity. - * - * @throws ORMInvalidArgumentException - * @throws ORMException + * @psalm-return never */ - public function merge($entity): object + public function merge($object): object { - Deprecation::trigger( - 'doctrine/orm', - 'https://github.com/doctrine/orm/issues/8461', - 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0.', - __METHOD__ - ); - - if (! is_object($entity)) { - throw ORMInvalidArgumentException::invalidObject('EntityManager#merge()', $entity); - } - - $this->errorIfClosed(); - - return $this->unitOfWork->merge($entity); + throw new BadMethodCallException('The merge operation is not supported.'); } /** diff --git a/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php b/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php deleted file mode 100644 index c1bfe8553a8..00000000000 --- a/lib/Doctrine/ORM/Mapping/Reflection/ReflectionPropertiesGetter.php +++ /dev/null @@ -1,135 +0,0 @@ -reflectionService = $reflectionService; - } - - /** - * @param string $className - * @psalm-param class-string $className - * - * @return ReflectionProperty[] indexed by property internal name - */ - public function getProperties($className): array - { - if (isset($this->properties[$className])) { - return $this->properties[$className]; - } - - return $this->properties[$className] = call_user_func_array( - 'array_merge', - // first merge because `array_merge` expects >= 1 params - array_merge( - [[]], - array_map( - [$this, 'getClassProperties'], - $this->getHierarchyClasses($className) - ) - ) - ); - } - - /** - * @psalm-param class-string $className - * - * @return ReflectionClass[] - * @psalm-return list> - */ - private function getHierarchyClasses(string $className): array - { - $classes = []; - $parentClassName = $className; - - while ($parentClassName && $currentClass = $this->reflectionService->getClass($parentClassName)) { - $classes[] = $currentClass; - $parentClassName = null; - - $parentClass = $currentClass->getParentClass(); - if ($parentClass) { - $parentClassName = $parentClass->getName(); - } - } - - return $classes; - } - - // phpcs:disable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod - - /** - * @return ReflectionProperty[] - * @psalm-return array - */ - private function getClassProperties(ReflectionClass $reflectionClass): array - { - // phpcs:enable SlevomatCodingStandard.Classes.UnusedPrivateElements.UnusedMethod - $properties = $reflectionClass->getProperties(); - - return array_filter( - array_filter(array_map( - [$this, 'getAccessibleProperty'], - array_combine( - array_map([$this, 'getLogicalName'], $properties), - $properties - ) - )), - [$this, 'isInstanceProperty'] - ); - } - - private function isInstanceProperty(ReflectionProperty $reflectionProperty): bool - { - return ! $reflectionProperty->isStatic(); - } - - private function getAccessibleProperty(ReflectionProperty $property): ?ReflectionProperty - { - return $this->reflectionService->getAccessibleProperty( - $property->getDeclaringClass()->getName(), - $property->getName() - ); - } - - private function getLogicalName(ReflectionProperty $property): string - { - $propertyName = $property->getName(); - - if ($property->isPublic()) { - return $propertyName; - } - - if ($property->isProtected()) { - return "\0*\0" . $propertyName; - } - - return "\0" . $property->getDeclaringClass()->getName() . "\0" . $propertyName; - } -} diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 19d2ab9e67c..b513639c342 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -26,7 +26,6 @@ use Doctrine\ORM\Internal\HydrationCompleteHandler; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\MappingException; -use Doctrine\ORM\Mapping\Reflection\ReflectionPropertiesGetter; use Doctrine\ORM\Persisters\Collection\CollectionPersister; use Doctrine\ORM\Persisters\Collection\ManyToManyPersister; use Doctrine\ORM\Persisters\Collection\OneToManyPersister; @@ -35,7 +34,6 @@ use Doctrine\ORM\Persisters\Entity\JoinedSubclassPersister; use Doctrine\ORM\Persisters\Entity\SingleTablePersister; use Doctrine\ORM\Utility\IdentifierFlattener; -use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Persistence\NotifyPropertyChanged; use Doctrine\Persistence\ObjectManagerAware; use Doctrine\Persistence\PropertyChangedListener; @@ -228,11 +226,6 @@ class UnitOfWork implements PropertyChangedListener */ private array $visitedCollections = []; - /** - * The EntityManager that "owns" this UnitOfWork instance. - */ - private EntityManagerInterface $em; - /** * The entity persister instances used to persist entity instances. * @@ -290,20 +283,19 @@ class UnitOfWork implements PropertyChangedListener */ private HydrationCompleteHandler $hydrationCompleteHandler; - private ReflectionPropertiesGetter $reflectionPropertiesGetter; - /** * Initializes a new UnitOfWork instance, bound to the given EntityManager. + * + * @param EntityManagerInterface $em The EntityManager that "owns" this UnitOfWork instance. */ - public function __construct(EntityManagerInterface $em) - { - $this->em = $em; - $this->evm = $em->getEventManager(); - $this->listenersInvoker = new ListenersInvoker($em); - $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); - $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); - $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); - $this->reflectionPropertiesGetter = new ReflectionPropertiesGetter(new RuntimeReflectionService()); + public function __construct( + private EntityManagerInterface $em + ) { + $this->evm = $em->getEventManager(); + $this->listenersInvoker = new ListenersInvoker($em); + $this->hasCache = $em->getConfiguration()->isSecondLevelCacheEnabled(); + $this->identifierFlattener = new IdentifierFlattener($this, $em->getMetadataFactory()); + $this->hydrationCompleteHandler = new HydrationCompleteHandler($this->listenersInvoker, $em); } /** @@ -1724,198 +1716,6 @@ private function doRemove(object $entity, array &$visited): void } } - /** - * Merges the state of the given detached entity into this UnitOfWork. - * - * @deprecated 2.7 This method is being removed from the ORM and won't have any replacement - * - * @return object The managed copy of the entity. - * - * @throws OptimisticLockException If the entity uses optimistic locking through a version - * attribute and the version check against the managed copy fails. - */ - public function merge(object $entity): object - { - $visited = []; - - return $this->doMerge($entity, $visited); - } - - /** - * Executes a merge operation on an entity. - * - * @param string[] $assoc - * @psalm-param array $visited - * - * @return object The managed copy of the entity. - * - * @throws OptimisticLockException If the entity uses optimistic locking through a version - * attribute and the version check against the managed copy fails. - * @throws ORMInvalidArgumentException If the entity instance is NEW. - * @throws EntityNotFoundException if an assigned identifier is used in the entity, but none is provided. - */ - private function doMerge( - object $entity, - array &$visited, - ?object $prevManagedCopy = null, - array $assoc = [] - ): object { - $oid = spl_object_id($entity); - - if (isset($visited[$oid])) { - $managedCopy = $visited[$oid]; - - if ($prevManagedCopy !== null) { - $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); - } - - return $managedCopy; - } - - $class = $this->em->getClassMetadata(get_class($entity)); - - // First we assume DETACHED, although it can still be NEW but we can avoid - // an extra db-roundtrip this way. If it is not MANAGED but has an identity, - // we need to fetch it from the db anyway in order to merge. - // MANAGED entities are ignored by the merge operation. - $managedCopy = $entity; - - if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { - // Try to look the entity up in the identity map. - $id = $class->getIdentifierValues($entity); - - // If there is no ID, it is actually NEW. - if (! $id) { - $managedCopy = $this->newInstance($class); - - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - $this->persistNew($class, $managedCopy); - } else { - $flatId = $class->containsForeignIdentifier - ? $this->identifierFlattener->flattenIdentifier($class, $id) - : $id; - - $managedCopy = $this->tryGetById($flatId, $class->rootEntityName); - - if ($managedCopy) { - // We have the entity in-memory already, just make sure its not removed. - if ($this->getEntityState($managedCopy) === self::STATE_REMOVED) { - throw ORMInvalidArgumentException::entityIsRemoved($managedCopy, 'merge'); - } - } else { - // We need to fetch the managed copy in order to merge. - $managedCopy = $this->em->find($class->name, $flatId); - } - - if ($managedCopy === null) { - // If the identifier is ASSIGNED, it is NEW, otherwise an error - // since the managed entity was not found. - if (! $class->isIdentifierNatural()) { - throw EntityNotFoundException::fromClassNameAndIdentifier( - $class->getName(), - $this->identifierFlattener->flattenIdentifier($class, $id) - ); - } - - $managedCopy = $this->newInstance($class); - $class->setIdentifierValues($managedCopy, $id); - - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - $this->persistNew($class, $managedCopy); - } else { - $this->ensureVersionMatch($class, $entity, $managedCopy); - $this->mergeEntityStateIntoManagedCopy($entity, $managedCopy); - } - } - - $visited[$oid] = $managedCopy; // mark visited - - if ($class->isChangeTrackingDeferredExplicit()) { - $this->scheduleForDirtyCheck($entity); - } - } - - if ($prevManagedCopy !== null) { - $this->updateAssociationWithMergedEntity($entity, $assoc, $prevManagedCopy, $managedCopy); - } - - // Mark the managed copy visited as well - $visited[spl_object_id($managedCopy)] = $managedCopy; - - $this->cascadeMerge($entity, $managedCopy, $visited); - - return $managedCopy; - } - - /** - * @psalm-param ClassMetadata $class - * @psalm-param T $entity - * @psalm-param T $managedCopy - * - * @throws OptimisticLockException - * - * @template T of object - */ - private function ensureVersionMatch( - ClassMetadata $class, - object $entity, - object $managedCopy - ): void { - if (! ($class->isVersioned && $this->isLoaded($managedCopy) && $this->isLoaded($entity))) { - return; - } - - $reflField = $class->reflFields[$class->versionField]; - $managedCopyVersion = $reflField->getValue($managedCopy); - $entityVersion = $reflField->getValue($entity); - - // Throw exception if versions don't match. - // phpcs:ignore SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedEqualOperator - if ($managedCopyVersion == $entityVersion) { - return; - } - - throw OptimisticLockException::lockFailedVersionMismatch($entity, $entityVersion, $managedCopyVersion); - } - - /** - * Tests if an entity is loaded - must either be a loaded proxy or not a proxy - */ - private function isLoaded(object $entity): bool - { - return ! ($entity instanceof Proxy) || $entity->__isInitialized(); - } - - /** - * Sets/adds associated managed copies into the previous entity's association field - * - * @param string[] $association - */ - private function updateAssociationWithMergedEntity( - object $entity, - array $association, - object $previousManagedCopy, - object $managedCopy - ): void { - $assocField = $association['fieldName']; - $prevClass = $this->em->getClassMetadata(get_class($previousManagedCopy)); - - if ($association['type'] & ClassMetadata::TO_ONE) { - $prevClass->reflFields[$assocField]->setValue($previousManagedCopy, $managedCopy); - - return; - } - - $value = $prevClass->reflFields[$assocField]->getValue($previousManagedCopy); - $value[] = $managedCopy; - - if ($association['type'] === ClassMetadata::ONE_TO_MANY) { - $class = $this->em->getClassMetadata(get_class($entity)); - - $class->reflFields[$association['mappedBy']]->setValue($managedCopy, $previousManagedCopy); - } - } - /** * Detaches an entity from the persistence management. It's persistence will * no longer be managed by Doctrine. @@ -2101,44 +1901,6 @@ static function (array $assoc) { } } - /** - * Cascades a merge operation to associated entities. - * - * @psalm-param array $visited - */ - private function cascadeMerge(object $entity, object $managedCopy, array &$visited): void - { - $class = $this->em->getClassMetadata(get_class($entity)); - - $associationMappings = array_filter( - $class->associationMappings, - static function (array $assoc) { - return $assoc['isCascadeMerge']; - } - ); - - foreach ($associationMappings as $assoc) { - $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); - - if ($relatedEntities instanceof Collection) { - if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { - continue; - } - - if ($relatedEntities instanceof PersistentCollection) { - // Unwrap so that foreach() does not initialize - $relatedEntities = $relatedEntities->unwrap(); - } - - foreach ($relatedEntities as $relatedEntity) { - $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); - } - } elseif ($relatedEntities !== null) { - $this->doMerge($relatedEntities, $visited, $managedCopy, $assoc); - } - } - } - /** * Cascades the save operation to associated entities. * @@ -3194,112 +2956,6 @@ private function assertThatThereAreNoUnintentionallyNonPersistedAssociations(): } } - /** - * @throws ORMException - * @throws OptimisticLockException - * @throws TransactionRequiredException - */ - private function mergeEntityStateIntoManagedCopy(object $entity, object $managedCopy): void - { - if (! $this->isLoaded($entity)) { - return; - } - - if (! $this->isLoaded($managedCopy)) { - $managedCopy->__load(); - } - - $class = $this->em->getClassMetadata(get_class($entity)); - - foreach ($this->reflectionPropertiesGetter->getProperties($class->name) as $prop) { - $name = $prop->name; - - $prop->setAccessible(true); - - if (! isset($class->associationMappings[$name])) { - if (! $class->isIdentifier($name)) { - $prop->setValue($managedCopy, $prop->getValue($entity)); - } - } else { - $assoc2 = $class->associationMappings[$name]; - - if ($assoc2['type'] & ClassMetadata::TO_ONE) { - $other = $prop->getValue($entity); - if ($other === null) { - $prop->setValue($managedCopy, null); - } else { - if ($other instanceof Proxy && ! $other->__isInitialized()) { - // do not merge fields marked lazy that have not been fetched. - continue; - } - - if (! $assoc2['isCascadeMerge']) { - if ($this->getEntityState($other) === self::STATE_DETACHED) { - $targetClass = $this->em->getClassMetadata($assoc2['targetEntity']); - $relatedId = $targetClass->getIdentifierValues($other); - - if ($targetClass->subClasses) { - $other = $this->em->find($targetClass->name, $relatedId); - } else { - $other = $this->em->getProxyFactory()->getProxy( - $assoc2['targetEntity'], - $relatedId - ); - $this->registerManaged($other, $relatedId, []); - } - } - - $prop->setValue($managedCopy, $other); - } - } - } else { - $mergeCol = $prop->getValue($entity); - - if ($mergeCol instanceof PersistentCollection && ! $mergeCol->isInitialized()) { - // do not merge fields marked lazy that have not been fetched. - // keep the lazy persistent collection of the managed copy. - continue; - } - - $managedCol = $prop->getValue($managedCopy); - - if (! $managedCol) { - $managedCol = new PersistentCollection( - $this->em, - $this->em->getClassMetadata($assoc2['targetEntity']), - new ArrayCollection() - ); - $managedCol->setOwner($managedCopy, $assoc2); - $prop->setValue($managedCopy, $managedCol); - } - - if ($assoc2['isCascadeMerge']) { - $managedCol->initialize(); - - // clear and set dirty a managed collection if its not also the same collection to merge from. - if (! $managedCol->isEmpty() && $managedCol !== $mergeCol) { - $managedCol->unwrap()->clear(); - $managedCol->setDirty(true); - - if ( - $assoc2['isOwningSide'] - && $assoc2['type'] === ClassMetadata::MANY_TO_MANY - && $class->isChangeTrackingNotify() - ) { - $this->scheduleForDirtyCheck($managedCopy); - } - } - } - } - } - - if ($class->isChangeTrackingNotify()) { - // Just treat all properties as changed, there is no other choice. - $this->propertyChanged($managedCopy, $name, null, $prop->getValue($managedCopy)); - } - } - } - /** * This method called by hydrators, and indicates that hydrator totally completed current hydration cycle. * Unit of work able to fire deferred events, related to loading events here. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e5f7b802fad..4d275f3b11d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1400,11 +1400,6 @@ parameters: count: 1 path: lib/Doctrine/ORM/Tools/SchemaTool.php - - - message: "#^Binary operation \"&\" between string and 3 results in an error\\.$#" - count: 1 - path: lib/Doctrine/ORM/UnitOfWork.php - - message: "#^Call to an undefined method Doctrine\\\\Common\\\\Collections\\\\Collection\\<\\(int\\|string\\), object\\>\\:\\:getMapping\\(\\)\\.$#" count: 2 @@ -1430,11 +1425,6 @@ parameters: count: 1 path: lib/Doctrine/ORM/UnitOfWork.php - - - message: "#^Strict comparison using \\=\\=\\= between string and 4 will always evaluate to false\\.$#" - count: 1 - path: lib/Doctrine/ORM/UnitOfWork.php - - message: "#^Access to an undefined property Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadata\\:\\:\\$name\\.$#" count: 1 diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3b0c2b33c6d..668f827b87a 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -244,14 +244,12 @@ ProxyFactory ProxyFactory - + getAutoGenerateProxyClasses getProxyDir getProxyNamespace - merge - - is_object($entity) + is_object($entity) is_object($entity) is_object($entity) @@ -282,8 +280,7 @@ EntityRepository<T> - - $entity + $entity $entity $entity @@ -2800,25 +2797,17 @@ $collectionToUpdate $commitOrder[$i] - - $association['type'] === ClassMetadata::ONE_TO_MANY - $association['type'] === ClassMetadata::ONE_TO_MANY - $em->getMetadataFactory() object - - $association['type'] - $this->entityChangeSets $this->entityChangeSets - - static function (array $assoc) { + static function (array $assoc) { static function (array $assoc) { static function (array $assoc) { @@ -2845,7 +2834,7 @@ $assoc['targetEntity'] $assoc['type'] - + buildCachedCollectionPersister buildCachedEntityPersister getCacheFactory @@ -2858,12 +2847,6 @@ getValue getValue getValue - getValue - getValue - getValue - getValue - setValue - setValue setValue setValue setValue diff --git a/tests/Doctrine/Tests/ORM/EntityManagerTest.php b/tests/Doctrine/Tests/ORM/EntityManagerTest.php index 52883c64f2b..4a63c50c951 100644 --- a/tests/Doctrine/Tests/ORM/EntityManagerTest.php +++ b/tests/Doctrine/Tests/ORM/EntityManagerTest.php @@ -129,7 +129,6 @@ public static function dataMethodsAffectedByNoObjectArguments(): array return [ ['persist'], ['remove'], - ['merge'], ['refresh'], ['detach'], ]; @@ -155,7 +154,6 @@ public static function dataAffectedByErrorIfClosedException(): array ['flush'], ['persist'], ['remove'], - ['merge'], ['refresh'], ]; } diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index e71532de57b..d264a635b34 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -5,7 +5,6 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\EntityNotFoundException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\PersistentCollection; @@ -900,97 +899,6 @@ public function testGetPartialReferenceToUpdateObjectWithoutLoadingIt(): void self::assertEquals('Benjamin E.', $this->_em->find(get_class($user), $userId)->name); } - public function testMergePersistsNewEntities(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - - $managedUser = $this->_em->merge($user); - self::assertEquals('beberlei', $managedUser->username); - self::assertEquals('Benjamin E.', $managedUser->name); - self::assertEquals('active', $managedUser->status); - - self::assertTrue($user !== $managedUser); - self::assertTrue($this->_em->contains($managedUser)); - - $this->_em->flush(); - $userId = $managedUser->id; - $this->_em->clear(); - - $user2 = $this->_em->find(get_class($managedUser), $userId); - self::assertInstanceOf(CmsUser::class, $user2); - } - - public function testMergeNonPersistedProperties(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - $user->nonPersistedProperty = 'test'; - $user->nonPersistedPropertyObject = new CmsPhonenumber(); - - $managedUser = $this->_em->merge($user); - self::assertEquals('test', $managedUser->nonPersistedProperty); - self::assertSame($user->nonPersistedProperty, $managedUser->nonPersistedProperty); - self::assertSame($user->nonPersistedPropertyObject, $managedUser->nonPersistedPropertyObject); - - self::assertTrue($user !== $managedUser); - self::assertTrue($this->_em->contains($managedUser)); - - $this->_em->flush(); - $userId = $managedUser->id; - $this->_em->clear(); - - $user2 = $this->_em->find(get_class($managedUser), $userId); - self::assertNull($user2->nonPersistedProperty); - self::assertNull($user2->nonPersistedPropertyObject); - self::assertEquals('active', $user2->status); - } - - public function testMergeThrowsExceptionIfEntityWithGeneratedIdentifierDoesNotExist(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - $user->id = 42; - - $this->expectException(EntityNotFoundException::class); - $this->_em->merge($user); - } - - /** - * @group DDC-634 - */ - public function testOneToOneMergeSetNull(): void - { - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin E.'; - $user->status = 'active'; - - $ph = new CmsPhonenumber(); - $ph->phonenumber = '12345'; - $user->addPhonenumber($ph); - - $this->_em->persist($user); - $this->_em->persist($ph); - $this->_em->flush(); - - $this->_em->clear(); - - $ph->user = null; - $managedPh = $this->_em->merge($ph); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertNull($this->_em->find(get_class($ph), $ph->phonenumber)->getUser()); - } - /** * @group DDC-952 */ diff --git a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php index 463eef39003..37461ed8f1c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/DetachedEntityTest.php @@ -5,19 +5,10 @@ namespace Doctrine\Tests\ORM\Functional; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use Doctrine\ORM\OptimisticLockException; -use Doctrine\ORM\Proxy\Proxy; -use Doctrine\Tests\Models\CMS\CmsAddress; -use Doctrine\Tests\Models\CMS\CmsArticle; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\OrmFunctionalTestCase; -use function count; -use function get_class; -use function serialize; -use function unserialize; - /** * Description of DetachedEntityTest */ @@ -29,89 +20,6 @@ protected function setUp(): void parent::setUp(); } - public function testSimpleDetachMerge(): void - { - $user = new CmsUser(); - $user->name = 'Roman'; - $user->username = 'romanb'; - $user->status = 'dev'; - $this->_em->persist($user); - $this->_em->flush(); - $this->_em->clear(); - - // $user is now detached - self::assertFalse($this->_em->contains($user)); - - $user->name = 'Roman B.'; - - $user2 = $this->_em->merge($user); - - self::assertFalse($user === $user2); - self::assertTrue($this->_em->contains($user2)); - self::assertEquals('Roman B.', $user2->name); - } - - public function testSerializeUnserializeModifyMerge(): void - { - $user = new CmsUser(); - $user->name = 'Guilherme'; - $user->username = 'gblanco'; - $user->status = 'developer'; - - $ph1 = new CmsPhonenumber(); - $ph1->phonenumber = '1234'; - $user->addPhonenumber($ph1); - - $this->_em->persist($user); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user)); - self::assertTrue($user->phonenumbers->isInitialized()); - - $serialized = serialize($user); - - $this->_em->clear(); - - self::assertFalse($this->_em->contains($user)); - - unset($user); - - $user = unserialize($serialized); - - self::assertEquals(1, count($user->getPhonenumbers()), 'Pre-Condition: 1 Phonenumber'); - - $ph2 = new CmsPhonenumber(); - - $ph2->phonenumber = '56789'; - $user->addPhonenumber($ph2); - - $oldPhonenumbers = $user->getPhonenumbers(); - - self::assertEquals(2, count($oldPhonenumbers), 'Pre-Condition: 2 Phonenumbers'); - self::assertFalse($this->_em->contains($user)); - - $this->_em->persist($ph2); - - // Merge back in - $user = $this->_em->merge($user); // merge cascaded to phonenumbers - self::assertInstanceOf(CmsUser::class, $user->phonenumbers[0]->user); - self::assertInstanceOf(CmsUser::class, $user->phonenumbers[1]->user); - $im = $this->_em->getUnitOfWork()->getIdentityMap(); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user), 'Failed to assert that merged user is contained inside EntityManager persistence context.'); - $phonenumbers = $user->getPhonenumbers(); - self::assertNotSame($oldPhonenumbers, $phonenumbers, 'Merge should replace the Detached Collection with a new PersistentCollection.'); - self::assertEquals(2, count($phonenumbers), 'Failed to assert that two phonenumbers are contained in the merged users phonenumber collection.'); - - self::assertInstanceOf(CmsPhonenumber::class, $phonenumbers[1]); - self::assertTrue($this->_em->contains($phonenumbers[1]), 'Failed to assert that second phonenumber in collection is contained inside EntityManager persistence context.'); - - self::assertInstanceOf(CmsPhonenumber::class, $phonenumbers[0]); - self::assertTrue($this->_em->getUnitOfWork()->isInIdentityMap($phonenumbers[0])); - self::assertTrue($this->_em->contains($phonenumbers[0]), 'Failed to assert that first phonenumber in collection is contained inside EntityManager persistence context.'); - } - /** * @group DDC-203 */ @@ -131,38 +39,6 @@ public function testDetachedEntityThrowsExceptionOnFlush(): void $this->_em->flush(); } - public function testUninitializedLazyAssociationsAreIgnoredOnMerge(): void - { - $user = new CmsUser(); - $user->name = 'Guilherme'; - $user->username = 'gblanco'; - $user->status = 'developer'; - - $address = new CmsAddress(); - $address->city = 'Berlin'; - $address->country = 'Germany'; - $address->street = 'Sesamestreet'; - $address->zip = 12345; - $address->setUser($user); - $this->_em->persist($address); - $this->_em->persist($user); - - $this->_em->flush(); - $this->_em->clear(); - - $address2 = $this->_em->find(get_class($address), $address->id); - self::assertInstanceOf(Proxy::class, $address2->user); - self::assertFalse($address2->user->__isInitialized__); - $detachedAddress2 = unserialize(serialize($address2)); - self::assertInstanceOf(Proxy::class, $detachedAddress2->user); - self::assertFalse($detachedAddress2->user->__isInitialized__); - - $managedAddress2 = $this->_em->merge($detachedAddress2); - self::assertInstanceOf(Proxy::class, $managedAddress2->user); - self::assertFalse($managedAddress2->user === $detachedAddress2->user); - self::assertFalse($managedAddress2->user->__isInitialized__); - } - /** * @group DDC-822 */ @@ -206,27 +82,4 @@ public function testDetachManagedUnpersistedEntity(): void self::assertFalse($this->_em->contains($user)); self::assertFalse($this->_em->getUnitOfWork()->isInIdentityMap($user)); } - - /** - * @group DDC-1340 - */ - public function testMergeArticleWrongVersion(): void - { - $article = new CmsArticle(); - $article->topic = 'test'; - $article->text = 'test'; - - $this->_em->persist($article); - $this->_em->flush(); - - $this->_em->detach($article); - - $sql = 'UPDATE cms_articles SET version = version + 1 WHERE id = ' . $article->id; - $this->_em->getConnection()->executeStatement($sql); - - $this->expectException(OptimisticLockException::class); - $this->expectExceptionMessage('The optimistic lock failed, version 1 was expected, but is actually 2'); - - $this->_em->merge($article); - } } diff --git a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php index 3a125acc4fa..a4237dfdb1c 100644 --- a/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/EntityRepositoryTest.php @@ -939,7 +939,7 @@ public function testMatchingCriteriaEndsWithComparison(): void public function testMatchingCriteriaNullAssocComparison(): void { $fixtures = $this->loadFixtureUserEmail(); - $user = $this->_em->merge($fixtures[0]); + $user = $this->_em->find(CmsUser::class, $fixtures[0]->id); $repository = $this->_em->getRepository(CmsUser::class); $criteriaIsNull = Criteria::create()->where(Criteria::expr()->isNull('email')); $criteriaEqNull = Criteria::create()->where(Criteria::expr()->eq('email', null)); diff --git a/tests/Doctrine/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php b/tests/Doctrine/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php deleted file mode 100644 index e4672d9ffe1..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/MergeCompositeToOneKeyTest.php +++ /dev/null @@ -1,48 +0,0 @@ -_schemaTool->createSchema( - [ - $this->_em->getClassMetadata(Country::class), - $this->_em->getClassMetadata(CompositeToOneKeyState::class), - ] - ); - } - - /** - * @group DDC-3378 - * @group 1176 - */ - public function testMergingOfEntityWithCompositeIdentifierContainingToOneAssociation(): void - { - $country = new Country(); - $country->country = 'US'; - - $state = new CompositeToOneKeyState(); - $state->state = 'CA'; - $state->country = $country; - - $merged = $this->_em->merge($state); - assert($merged instanceof CompositeToOneKeyState); - - self::assertInstanceOf(CompositeToOneKeyState::class, $state); - self::assertNotSame($state, $merged); - self::assertInstanceOf(Country::class, $merged->country); - self::assertNotSame($country, $merged->country); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php b/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php deleted file mode 100644 index 5a7bc9bc805..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/MergeProxiesTest.php +++ /dev/null @@ -1,274 +0,0 @@ -useModelSet('generic'); - - parent::setUp(); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeDetachedUnInitializedProxy(): void - { - $detachedUninitialized = $this->_em->getReference(DateTimeModel::class, 123); - - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame($managed, $this->_em->merge($detachedUninitialized)); - - self::assertFalse($managed->__isInitialized()); - self::assertFalse($detachedUninitialized->__isInitialized()); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeUnserializedUnInitializedProxy(): void - { - $detachedUninitialized = $this->_em->getReference(DateTimeModel::class, 123); - - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame( - $managed, - $this->_em->merge(unserialize(serialize($this->_em->merge($detachedUninitialized)))) - ); - - self::assertFalse($managed->__isInitialized()); - self::assertFalse($detachedUninitialized->__isInitialized()); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergeManagedProxy(): void - { - $managed = $this->_em->getReference(DateTimeModel::class, 123); - - self::assertSame($managed, $this->_em->merge($managed)); - - self::assertFalse($managed->__isInitialized()); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - * - * Bug discovered while working on DDC-2704 - merging towards un-initialized proxies does not initialize them, - * causing merged data to be lost when they are actually initialized - */ - public function testMergeWithExistingUninitializedManagedProxy(): void - { - $date = new DateTimeModel(); - - $this->_em->persist($date); - $this->_em->flush(); - $this->_em->clear(); - - $managed = $this->_em->getReference(DateTimeModel::class, $date->id); - - self::assertInstanceOf(Proxy::class, $managed); - self::assertFalse($managed->__isInitialized()); - - $date->date = $dateTime = new DateTime(); - - self::assertSame($managed, $this->_em->merge($date)); - self::assertTrue($managed->__isInitialized()); - self::assertSame($dateTime, $managed->date, 'Data was merged into the proxy after initialization'); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergingProxyFromDifferentEntityManagerWithExistingManagedInstanceDoesNotReplaceInitializer(): void - { - $em1 = $this->createEntityManager(); - $em2 = $this->createEntityManager(); - - $file1 = new DateTimeModel(); - $file2 = new DateTimeModel(); - - $em1->persist($file1); - $em2->persist($file2); - $em1->flush(); - $em2->flush(); - $em1->clear(); - $em2->clear(); - - $logger1 = $this->getResetQueryLogFromEntityManager($em1); - $logger2 = $this->getResetQueryLogFromEntityManager($em2); - - $proxy1 = $em1->getReference(DateTimeModel::class, $file1->id); - $proxy2 = $em2->getReference(DateTimeModel::class, $file1->id); - $merged2 = $em2->merge($proxy1); - - self::assertNotSame($proxy1, $merged2); - self::assertSame($proxy2, $merged2); - - self::assertFalse($proxy1->__isInitialized()); - self::assertFalse($proxy2->__isInitialized()); - - $proxy1->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the first proxy was done through the first entity manager' - ); - self::assertCount( - 0, - $logger2->queries, - 'No queries were executed on the second entity manager, as it is unrelated with the first proxy' - ); - - $proxy2->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the second proxy does not affect the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'Loading of the second proxy instance was done through the second entity manager' - ); - } - - /** - * @group DDC-1392 - * @group DDC-1734 - * @group DDC-3368 - * @group #1172 - */ - public function testMergingUnInitializedProxyDoesNotInitializeIt(): void - { - $em1 = $this->createEntityManager(); - $em2 = $this->createEntityManager(); - - $file1 = new DateTimeModel(); - $file2 = new DateTimeModel(); - - $em1->persist($file1); - $em2->persist($file2); - $em1->flush(); - $em2->flush(); - $em1->clear(); - $em2->clear(); - - $logger1 = $this->getResetQueryLogFromEntityManager($em1); - $logger2 = $this->getResetQueryLogFromEntityManager($em2); - - $unManagedProxy = $em1->getReference(DateTimeModel::class, $file1->id); - $mergedInstance = $em2->merge($unManagedProxy); - - self::assertNotInstanceOf(Proxy::class, $mergedInstance); - self::assertNotSame($unManagedProxy, $mergedInstance); - self::assertFalse($unManagedProxy->__isInitialized()); - - self::assertCount( - 0, - $logger1->queries, - 'Loading the merged instance affected only the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'Loading the merged instance was done via the second entity manager' - ); - - $unManagedProxy->__load(); - - self::assertCount( - 1, - $logger1->queries, - 'Loading the first proxy was done through the first entity manager' - ); - self::assertCount( - 1, - $logger2->queries, - 'No queries were executed on the second entity manager, as it is unrelated with the first proxy' - ); - } - - private function createEntityManager(): EntityManagerInterface - { - $config = new Configuration(); - - $config->setProxyDir(realpath(__DIR__ . '/../../Proxies')); - $config->setProxyNamespace('Doctrine\Tests\Proxies'); - $config->setMetadataDriverImpl(ORMSetup::createDefaultAnnotationDriver( - [realpath(__DIR__ . '/../../Models/Cache')] - )); - - // always runs on sqlite to prevent multi-connection race-conditions with the test suite - // multi-connection is not relevant for the purpose of checking locking here, but merely - // to stub out DB-level access and intercept it - $connection = DriverManager::getConnection( - [ - 'driver' => 'pdo_sqlite', - 'memory' => true, - 'wrapperClass' => Connection::class, - ], - $config - ); - - $entityManager = EntityManager::create($connection, $config); - - (new SchemaTool($entityManager))->createSchema([$entityManager->getClassMetadata(DateTimeModel::class)]); - - return $entityManager; - } - - private function getResetQueryLogFromEntityManager(EntityManagerInterface $entityManager): QueryLog - { - $connection = $entityManager->getConnection(); - assert($connection instanceof Connection); - - return $connection->queryLog->reset()->enable(); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/MergeSharedEntitiesTest.php b/tests/Doctrine/Tests/ORM/Functional/MergeSharedEntitiesTest.php deleted file mode 100644 index e5b5705c12e..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/MergeSharedEntitiesTest.php +++ /dev/null @@ -1,170 +0,0 @@ -_schemaTool->createSchema( - [ - $this->_em->getClassMetadata(MSEFile::class), - $this->_em->getClassMetadata(MSEPicture::class), - ] - ); - } catch (ToolsException $ignored) { - } - } - - public function testMergeSharedNewEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $picture = $this->_em->merge($picture); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - public function testMergeSharedManagedEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $this->_em->persist($file); - $this->_em->persist($picture); - $this->_em->flush(); - $this->_em->clear(); - - $picture = $this->_em->merge($picture); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - public function testMergeSharedDetachedSerializedEntities(): void - { - $file = new MSEFile(); - $picture = new MSEPicture(); - - $picture->file = $file; - $picture->otherFile = $file; - - $serializedPicture = serialize($picture); - - $this->_em->persist($file); - $this->_em->persist($picture); - $this->_em->flush(); - $this->_em->clear(); - - $picture = $this->_em->merge(unserialize($serializedPicture)); - - self::assertEquals($picture->file, $picture->otherFile, 'Identical entities must remain identical'); - } - - /** - * @group DDC-2704 - */ - public function testMergeInheritedTransientPrivateProperties(): void - { - $admin1 = new MSEAdmin(); - $admin2 = new MSEAdmin(); - - $admin1->id = 123; - $admin2->id = 123; - - $this->_em->persist($admin1); - - $admin2->setSession('zeh current session data'); - - self::assertSame($admin1, $this->_em->merge($admin2)); - self::assertSame('zeh current session data', $admin1->getSession()); - } -} - -/** @Entity */ -class MSEPicture -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue - */ - public $id; - - /** - * @var MSEFile - * @ManyToOne(targetEntity="MSEFile", cascade={"merge"}) - */ - public $file; - - /** - * @var MSEFile - * @ManyToOne(targetEntity="MSEFile", cascade={"merge"}) - */ - public $otherFile; -} - -/** @Entity */ -class MSEFile -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $id; -} - -/** @MappedSuperclass */ -abstract class MSEUser -{ - /** @var string */ - private $session; // intentionally transient property - - public function getSession(): string - { - return $this->session; - } - - public function setSession(string $session): void - { - $this->session = $session; - } -} - -/** @Entity */ -class MSEAdmin extends MSEUser -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="NONE") - */ - public $id; -} diff --git a/tests/Doctrine/Tests/ORM/Functional/MergeVersionedManyToOneTest.php b/tests/Doctrine/Tests/ORM/Functional/MergeVersionedManyToOneTest.php deleted file mode 100644 index ae1ea0786b9..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/MergeVersionedManyToOneTest.php +++ /dev/null @@ -1,46 +0,0 @@ -useModelSet('versioned_many_to_one'); - - parent::setUp(); - } - - /** - * This test case asserts that a detached and unmodified entity could be merge without firing - * OptimisticLockException. - */ - public function testSetVersionOnCreate(): void - { - $category = new Category(); - $article = new Article(); - - $article->name = 'Article'; - $article->category = $category; - - $this->_em->persist($article); - $this->_em->flush(); - $this->_em->clear(); - - $articleMerged = $this->_em->merge($article); - - $articleMerged->name = 'Article Merged'; - - $this->_em->flush(); - self::assertEquals(2, $articleMerged->version); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php index 9a91fe7dd94..883e8108eab 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC117Test.php @@ -434,22 +434,6 @@ private function loadEditorFixture(): DDC117Editor return $this->_em->find(get_class($editor), $editor->id); } - /** - * @group DDC-1519 - */ - public function testMergeForeignKeyIdentifierEntity(): void - { - $idCriteria = ['source' => $this->article1->id(), 'target' => $this->article2->id()]; - - $refRep = $this->_em->find(DDC117Reference::class, $idCriteria); - - $this->_em->clear(); - $refRep = $this->_em->merge($refRep); - - self::assertEquals($this->article1->id(), $refRep->source()->id()); - self::assertEquals($this->article2->id(), $refRep->target()->id()); - } - /** * @group DDC-1652 */ diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1276Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1276Test.php deleted file mode 100644 index 600b49ca5f3..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1276Test.php +++ /dev/null @@ -1,53 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - public function testIssue(): void - { - $user = new CmsUser(); - $user->name = 'Benjamin'; - $user->username = 'beberlei'; - $user->status = 'active'; - $this->_em->persist($user); - - for ($i = 0; $i < 2; $i++) { - $group = new CmsGroup(); - $group->name = 'group' . $i; - $user->groups[] = $group; - $this->_em->persist($group); - } - - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(CmsUser::class, $user->id); - $cloned = clone $user; - - self::assertSame($user->groups, $cloned->groups); - self::assertEquals(2, count($user->groups)); - $this->_em->merge($cloned); - - self::assertEquals(2, count($user->groups)); - - $this->_em->flush(); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php deleted file mode 100644 index 828d493d6d9..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1383Test.php +++ /dev/null @@ -1,120 +0,0 @@ -_schemaTool->createSchema( - [ - $this->_em->getClassMetadata(DDC1383AbstractEntity::class), - $this->_em->getClassMetadata(DDC1383Entity::class), - ] - ); - } catch (Exception $ignored) { - } - } - - public function testFailingCase(): void - { - $parent = new DDC1383Entity(); - $child = new DDC1383Entity(); - - $child->setReference($parent); - - $this->_em->persist($parent); - $this->_em->persist($child); - - $id = $child->getId(); - - $this->_em->flush(); - $this->_em->clear(); - - // Try merging the parent entity - $child = $this->_em->merge($child); - $parent = $child->getReference(); - - // Parent is not instance of the abstract class - self::assertTrue( - $parent instanceof DDC1383AbstractEntity, - 'Entity class is ' . get_debug_type($parent) . ', "DDC1383AbstractEntity" was expected' - ); - - // Parent is NOT instance of entity - self::assertTrue( - $parent instanceof DDC1383Entity, - 'Entity class is ' . get_debug_type($parent) . ', "DDC1383Entity" was expected' - ); - } -} - -/** - * @Entity - * @InheritanceType("JOINED") - * @DiscriminatorColumn(name="discr", type="integer") - * @DiscriminatorMap({1 = "DDC1383Entity"}) - */ -abstract class DDC1383AbstractEntity -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue - */ - protected $id; - - public function getId(): ?int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; - } -} - -/** - * @Entity - */ -class DDC1383Entity extends DDC1383AbstractEntity -{ - /** - * @var DDC1383AbstractEntity - * @ManyToOne(targetEntity="DDC1383AbstractEntity") - */ - protected $reference; - - public function getReference(): DDC1383AbstractEntity - { - return $this->reference; - } - - public function setReference(DDC1383AbstractEntity $reference): void - { - $this->reference = $reference; - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1392Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1392Test.php deleted file mode 100644 index 05744bc93ac..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1392Test.php +++ /dev/null @@ -1,130 +0,0 @@ -_schemaTool->createSchema( - [ - $this->_em->getClassMetadata(DDC1392File::class), - $this->_em->getClassMetadata(DDC1392Picture::class), - ] - ); - } catch (Exception $ignored) { - } - } - - public function testFailingCase(): void - { - $file = new DDC1392File(); - - $picture = new DDC1392Picture(); - $picture->setFile($file); - - $em = $this->_em; - $em->persist($picture); - $em->flush(); - $em->clear(); - - $fileId = $file->getFileId(); - $pictureId = $picture->getPictureId(); - - self::assertTrue($fileId > 0); - - $picture = $em->find(DDC1392Picture::class, $pictureId); - self::assertEquals(UnitOfWork::STATE_MANAGED, $em->getUnitOfWork()->getEntityState($picture->getFile()), 'Lazy Proxy should be marked MANAGED.'); - - $file = $picture->getFile(); - - // With this activated there will be no problem - //$file->__load(); - - $picture->setFile(null); - - $em->clear(); - - $em->merge($file); - - $em->flush(); - - $q = $this->_em->createQuery('SELECT COUNT(e) FROM ' . __NAMESPACE__ . '\DDC1392File e'); - $result = $q->getSingleScalarResult(); - - self::assertEquals(1, $result); - } -} - -/** - * @Entity - */ -class DDC1392Picture -{ - /** - * @var int - * @Column(name="picture_id", type="integer") - * @Id - * @GeneratedValue - */ - private $pictureId; - - /** - * @var DDC1392File - * @ManyToOne(targetEntity="DDC1392File", cascade={"persist", "remove"}) - * @JoinColumn(name="file_id", referencedColumnName="file_id") - */ - private $file; - - public function getPictureId(): int - { - return $this->pictureId; - } - - public function setFile(?DDC1392File $value = null): void - { - $this->file = $value; - } - - public function getFile(): ?DDC1392File - { - return $this->file; - } -} - -/** - * @Entity - */ -class DDC1392File -{ - /** - * @var int - * @Column(name="file_id", type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $fileId; - - public function getFileId(): int - { - return $this->fileId; - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php deleted file mode 100644 index 10ba0c85bae..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php +++ /dev/null @@ -1,145 +0,0 @@ -_schemaTool->createSchema( - [ - $this->_em->getClassMetadata(DDC1509AbstractFile::class), - $this->_em->getClassMetadata(DDC1509File::class), - $this->_em->getClassMetadata(DDC1509Picture::class), - ] - ); - } catch (Exception $ignored) { - } - } - - public function testFailingCase(): void - { - $file = new DDC1509File(); - $thumbnail = new DDC1509File(); - - $picture = new DDC1509Picture(); - $picture->setFile($file); - $picture->setThumbnail($thumbnail); - - $em = $this->_em; - assert($em instanceof EntityManager); - $em->persist($picture); - $em->flush(); - $em->clear(); - - $id = $picture->getPictureId(); - - $pic = $em->merge($picture); - assert($pic instanceof DDC1509Picture); - - self::assertNotNull($pic->getThumbnail()); - self::assertNotNull($pic->getFile()); - } -} - -/** - * @Entity - */ -class DDC1509Picture -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - private $id; - - /** - * @var DDC1509AbstractFile - * @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"}) - */ - private $thumbnail; - - /** - * @var DDC1509AbstractFile|null - * @ManyToOne(targetEntity="DDC1509AbstractFile", cascade={"persist", "remove"}) - */ - private $file; - - public function getPictureId(): int - { - return $this->id; - } - - public function setFile(?DDC1509AbstractFile $value = null): void - { - $this->file = $value; - } - - public function getFile(): ?DDC1509AbstractFile - { - return $this->file; - } - - public function getThumbnail(): DDC1509AbstractFile - { - return $this->thumbnail; - } - - public function setThumbnail(DDC1509AbstractFile $thumbnail): void - { - $this->thumbnail = $thumbnail; - } -} - -/** - * @Entity - * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"abstractFile" = "DDC1509AbstractFile", "file" = "DDC1509File"}) - */ -class DDC1509AbstractFile -{ - /** - * @var int - * @Column(type="integer") - * @Id - * @GeneratedValue(strategy="AUTO") - */ - public $id; - - public function getFileId(): int - { - return $this->id; - } -} - -/** - * @Entity - */ -class DDC1509File extends DDC1509AbstractFile -{ -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1594Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1594Test.php deleted file mode 100644 index d8e6fed7f0b..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1594Test.php +++ /dev/null @@ -1,46 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - public function testIssue(): void - { - $user = new CmsUser(); - $user->status = 'foo'; - $user->username = 'foo'; - $user->name = 'foo'; - - $this->_em->persist($user); - $this->_em->flush(); - - $this->_em->clear(); - $detachedUser = clone $user; - $detachedUser->name = 'bar'; - $detachedUser->status = 'bar'; - - $newUser = $this->_em->getReference(get_class($user), $user->id); - - $mergedUser = $this->_em->merge($detachedUser); - - self::assertNotSame($mergedUser, $detachedUser); - self::assertEquals('bar', $detachedUser->getName()); - self::assertEquals('bar', $mergedUser->getName()); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php deleted file mode 100644 index c5e776e1da0..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php +++ /dev/null @@ -1,88 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - /** - * This test is DDC-1734 minus the serialization, i.e. it works - * - * @group DDC-1734 - */ - public function testMergeWorksOnNonSerializedProxies(): void - { - $group = new CmsGroup(); - - $group->setName('Foo'); - $this->_em->persist($group); - $this->_em->flush(); - $this->_em->clear(); - - $proxy = $this->getProxy($group); - - self::assertInstanceOf(Proxy::class, $proxy); - self::assertFalse($proxy->__isInitialized()); - - $this->_em->detach($proxy); - $this->_em->clear(); - - $proxy = $this->_em->merge($proxy); - - self::assertEquals('Foo', $proxy->getName(), 'The entity is broken'); - } - - /** - * This test reproduces DDC-1734 which is: - * - A non-initialized proxy is detached and serialized (the identifier of the proxy is *not* serialized) - * - the object is deserialized and merged (to turn into an entity) - * - the entity is broken because it has no identifier and no field defined - * - * @group DDC-1734 - */ - public function testMergeWorksOnSerializedProxies(): void - { - $group = new CmsGroup(); - - $group->setName('Foo'); - $this->_em->persist($group); - $this->_em->flush(); - $this->_em->clear(); - - $proxy = $this->getProxy($group); - - self::assertInstanceOf(Proxy::class, $proxy); - self::assertFalse($proxy->__isInitialized()); - - $this->_em->detach($proxy); - $serializedProxy = serialize($proxy); - $this->_em->clear(); - - $unserializedProxy = $this->_em->merge(unserialize($serializedProxy)); - self::assertEquals('Foo', $unserializedProxy->getName(), 'The entity is broken'); - } - - private function getProxy(object $object): \Doctrine\Common\Proxy\Proxy - { - $metadataFactory = $this->_em->getMetadataFactory(); - $className = get_class($object); - $identifier = $metadataFactory->getMetadataFor($className)->getIdentifierValues($object); - - return $this->_em->getProxyFactory()->getProxy($className, $identifier); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2230Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2230Test.php index c8025dc6dc6..44d9155e020 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2230Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2230Test.php @@ -38,29 +38,6 @@ protected function setUp(): void } } - public function testNotifyTrackingNotCalledOnUninitializedProxies(): void - { - $insertedUser = new DDC2230User(); - $insertedUser->address = new DDC2230Address(); - - $this->_em->persist($insertedUser); - $this->_em->persist($insertedUser->address); - $this->_em->flush(); - $this->_em->clear(); - - $user = $this->_em->find(DDC2230User::class, $insertedUser->id); - - $this->_em->clear(); - - $mergedUser = $this->_em->merge($user); - - $address = $mergedUser->address; - assert($address instanceof Proxy); - - self::assertInstanceOf(Proxy::class, $address); - self::assertFalse($address->__isInitialized()); - } - public function testNotifyTrackingCalledOnProxyInitialization(): void { $insertedAddress = new DDC2230Address(); diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php deleted file mode 100644 index cbd075f78d6..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2409Test.php +++ /dev/null @@ -1,74 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - public function testIssue(): void - { - $em = $this->_em; - $uow = $em->getUnitOfWork(); - - $originalArticle = new CmsArticle(); - $originalUser = new CmsUser(); - - $originalArticle->topic = 'Unit Test'; - $originalArticle->text = 'How to write a test'; - - $originalUser->name = 'Doctrine Bot'; - $originalUser->username = 'DoctrineBot'; - $originalUser->status = 'active'; - - $originalUser->addArticle($originalArticle); - - $em->persist($originalUser); - $em->persist($originalArticle); - $em->flush(); - $em->clear(); - - $article = $em->find(CmsArticle::class, $originalArticle->id); - $user = new CmsUser(); - - $user->name = 'Doctrine Bot 2.0'; - $user->username = 'BotDoctrine2'; - $user->status = 'new'; - - $article->setAuthor($user); - - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalArticle)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($originalUser)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($article)); - self::assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); - - $em->clear(); - - $userMerged = $em->merge($user); - $articleMerged = $em->merge($article); - - self::assertEquals(UnitOfWork::STATE_NEW, $uow->getEntityState($user)); - self::assertEquals(UnitOfWork::STATE_DETACHED, $uow->getEntityState($article)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($userMerged)); - self::assertEquals(UnitOfWork::STATE_MANAGED, $uow->getEntityState($articleMerged)); - - self::assertNotSame($user, $userMerged); - self::assertNotSame($article, $articleMerged); - self::assertNotSame($userMerged, $articleMerged->user); - self::assertSame($user, $articleMerged->user); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2645Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2645Test.php deleted file mode 100644 index 4e12ee95fe5..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2645Test.php +++ /dev/null @@ -1,78 +0,0 @@ -id = 123; - - $foo = new DDC2645Foo(1, $bar, 'Foo'); - $foo2 = new DDC2645Foo(1, $bar, 'Bar'); - - $this->_em->persist($bar); - $this->_em->persist($foo); - - $foo3 = $this->_em->merge($foo2); - - self::assertSame($foo, $foo3); - self::assertEquals('Bar', $foo->name); - } -} - -/** @Entity */ -class DDC2645Foo -{ - /** - * @var int - * @Id - * @Column(type="integer") - */ - private $id; - - /** - * @var DDC2645Bar - * @Id - * @ManyToOne(targetEntity="DDC2645Bar") - */ - private $bar; - - /** - * @var string - * @Column - */ - public $name; - - public function __construct(int $id, DDC2645Bar $bar, string $name) - { - $this->id = $id; - $this->bar = $bar; - $this->name = $name; - } -} - -/** @Entity */ -class DDC2645Bar -{ - /** - * @var int - * @Id - * @Column(type="integer") - * @GeneratedValue(strategy="NONE") - */ - public $id; -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3699Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3699Test.php deleted file mode 100644 index 5d002ef01a9..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3699Test.php +++ /dev/null @@ -1,108 +0,0 @@ -useModelSet('ddc3699'); - - parent::setUp(); - } - - /** - * @group DDC-3699 - */ - public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToOneUninitializedAssociations(): void - { - $id = 1; - - $child = new DDC3699Child(); - - $child->id = $id; - $child->childField = 'childValue'; - $child->parentField = 'parentValue'; - - $relation = new DDC3699RelationOne(); - - $relation->id = $id; - $relation->child = $child; - $child->oneRelation = $relation; - - $this->_em->persist($relation); - $this->_em->persist($child); - $this->_em->flush(); - $this->_em->clear(); - - $unManagedChild = $this->_em->find(DDC3699Child::class, $id); - assert($unManagedChild instanceof DDC3699Child); - - $this->_em->detach($unManagedChild); - - // make it managed again - $this->_em->find(DDC3699Child::class, $id); - - $unManagedChild->childField = 'modifiedChildValue'; - $unManagedChild->parentField = 'modifiedParentValue'; - - $mergedChild = $this->_em->merge($unManagedChild); - assert($mergedChild instanceof DDC3699Child); - - self::assertSame($mergedChild->childField, 'modifiedChildValue'); - self::assertSame($mergedChild->parentField, 'modifiedParentValue'); - } - - /** - * @group DDC-3699 - */ - public function testMergingParentClassFieldsDoesNotStopMergingScalarFieldsForToManyUninitializedAssociations(): void - { - $id = 2; - - $child = new DDC3699Child(); - - $child->id = $id; - $child->childField = 'childValue'; - $child->parentField = 'parentValue'; - - $relation = new DDC3699RelationMany(); - - $relation->id = $id; - $relation->child = $child; - $child->relations[] = $relation; - - $this->_em->persist($relation); - $this->_em->persist($child); - $this->_em->flush(); - $this->_em->clear(); - - $unmanagedChild = $this->_em->find(DDC3699Child::class, $id); - assert($unmanagedChild instanceof DDC3699Child); - $this->_em->detach($unmanagedChild); - - // make it managed again - $this->_em->find(DDC3699Child::class, $id); - - $unmanagedChild->childField = 'modifiedChildValue'; - $unmanagedChild->parentField = 'modifiedParentValue'; - - $mergedChild = $this->_em->merge($unmanagedChild); - assert($mergedChild instanceof DDC3699Child); - - self::assertSame($mergedChild->childField, 'modifiedChildValue'); - self::assertSame($mergedChild->parentField, 'modifiedParentValue'); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php deleted file mode 100644 index 7abdfcaafbc..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC501Test.php +++ /dev/null @@ -1,112 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - public function testMergeUnitializedManyToManyAndOneToManyCollections(): void - { - // Create User - $user = $this->createAndPersistUser(); - $this->_em->flush(); - - self::assertTrue($this->_em->contains($user)); - $this->_em->clear(); - self::assertFalse($this->_em->contains($user)); - - unset($user); - - // Reload User from DB *without* any associations (i.e. an uninitialized PersistantCollection) - $userReloaded = $this->loadUserFromEntityManager(); - - self::assertTrue($this->_em->contains($userReloaded)); - $this->_em->clear(); - self::assertFalse($this->_em->contains($userReloaded)); - - // freeze and unfreeze - $userClone = unserialize(serialize($userReloaded)); - self::assertInstanceOf(CmsUser::class, $userClone); - - // detached user can't know about his phonenumbers - self::assertEquals(0, count($userClone->getPhonenumbers())); - self::assertFalse($userClone->getPhonenumbers()->isInitialized(), 'User::phonenumbers should not be marked initialized.'); - - // detached user can't know about his groups either - self::assertEquals(0, count($userClone->getGroups())); - self::assertFalse($userClone->getGroups()->isInitialized(), 'User::groups should not be marked initialized.'); - - // Merge back and flush - $userClone = $this->_em->merge($userClone); - - // Back in managed world I would expect to have my phonenumbers back but they aren't! - // Remember I didn't touch (and probably didn't need) them at all while in detached mode. - self::assertEquals(4, count($userClone->getPhonenumbers()), 'Phonenumbers are not available anymore'); - - // This works fine as long as cmUser::groups doesn't cascade "merge" - self::assertEquals(2, count($userClone->getGroups())); - - $this->_em->flush(); - $this->_em->clear(); - - self::assertFalse($this->_em->contains($userClone)); - - // Reload user from DB - $userFromEntityManager = $this->loadUserFromEntityManager(); - - //Strange: Now the phonenumbers are back again - self::assertEquals(4, count($userFromEntityManager->getPhonenumbers())); - - // This works fine as long as cmUser::groups doesn't cascade "merge" - // Otherwise group memberships are physically deleted now! - self::assertEquals(2, count($userClone->getGroups())); - } - - protected function createAndPersistUser(): CmsUser - { - $user = new CmsUser(); - $user->name = 'Luka'; - $user->username = 'lukacho'; - $user->status = 'developer'; - - foreach ([1111, 2222, 3333, 4444] as $number) { - $phone = new CmsPhonenumber(); - $phone->phonenumber = $number; - $user->addPhonenumber($phone); - } - - foreach (['Moshers', 'Headbangers'] as $groupName) { - $group = new CmsGroup(); - $group->setName($groupName); - $user->addGroup($group); - } - - $this->_em->persist($user); - - return $user; - } - - protected function loadUserFromEntityManager(): CmsUser - { - return $this->_em - ->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name like :name') - ->setParameter('name', 'Luka') - ->getSingleResult(); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC518Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC518Test.php deleted file mode 100644 index f1ef8b0e6f8..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC518Test.php +++ /dev/null @@ -1,41 +0,0 @@ -useModelSet('cms'); - parent::setUp(); - } - - public function testMergeWithRelatedNew(): void - { - $article = new CmsArticle(); - $article->text = 'foo'; - $article->topic = 'bar'; - - $this->_em->persist($article); - $this->_em->flush(); - $this->_em->detach($article); - $this->_em->clear(); - - $user = new CmsUser(); - $user->username = 'beberlei'; - $user->name = 'Benjamin Eberlei'; - $user->status = 'active'; - $article->user = $user; - - $this->_em->persist($user); - $managedArticle = $this->_em->merge($article); - - self::assertSame($article->user, $managedArticle->user); - } -} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php deleted file mode 100644 index 3754508b175..00000000000 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC729Test.php +++ /dev/null @@ -1,213 +0,0 @@ -_em); - $schemaTool->createSchema( - [ - $this->_em->getClassMetadata(DDC729A::class), - $this->_em->getClassMetadata(DDC729B::class), - ] - ); - } catch (Exception $e) { - } - } - - public function testMergeManyToMany(): void - { - $a = new DDC729A(); - $b = new DDC729B(); - $a->related[] = $b; - - $this->_em->persist($a); - $this->_em->persist($b); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - self::assertInstanceOf(ArrayCollection::class, $a->related); - - $a = $this->_em->merge($a); - - self::assertInstanceOf(PersistentCollection::class, $a->related); - - self::assertFalse($a->related->isInitialized(), 'Collection should not be marked initialized.'); - self::assertFalse($a->related->isDirty(), 'Collection should not be marked as dirty.'); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(1, count($a->related)); - } - - public function testUnidirectionalMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - - $a->related->set(1, $this->_em->merge($b2)); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } - - public function testBidirectionalMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - $b1->related->set(0, $a); - - $a->related->set(1, $this->_em->merge($b2)); - $b2->related->set(0, $a); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } - - public function testBidirectionalMultiMergeManyToMany(): void - { - $a = new DDC729A(); - $b1 = new DDC729B(); - $b2 = new DDC729B(); - $a->related[] = $b1; - - $this->_em->persist($a); - $this->_em->persist($b1); - $this->_em->persist($b2); - $this->_em->flush(); - $this->_em->clear(); - $aId = $a->id; - - $a = new DDC729A(); - $a->id = $aId; - - $a = $this->_em->merge($a); - - $a->related->set(0, $this->_em->merge($b1)); - $b1->related->set(0, $this->_em->merge($a)); - - $a->related->set(1, $this->_em->merge($b2)); - $b2->related->set(0, $this->_em->merge($a)); - - $this->_em->flush(); - $this->_em->clear(); - - $a = $this->_em->find(DDC729A::class, $aId); - self::assertEquals(2, count($a->related)); - } -} - -/** - * @Entity - */ -class DDC729A -{ - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - public $id; - - /** - * @psalm-var Collection - * @ManyToMany(targetEntity="DDC729B", inversedBy="related") - */ - public $related; - - public function __construct() - { - $this->related = new ArrayCollection(); - } -} - -/** - * @Entity - */ -class DDC729B -{ - /** - * @var int - * @Id - * @GeneratedValue - * @Column(type="integer") - */ - public $id; - - /** - * @psalm-var Collection - * @ManyToMany(targetEntity="DDC729B", mappedBy="related") - */ - public $related; - - public function __construct() - { - $this->related = new ArrayCollection(); - } -} diff --git a/tests/Doctrine/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php b/tests/Doctrine/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php deleted file mode 100644 index d608b939efc..00000000000 --- a/tests/Doctrine/Tests/ORM/Mapping/Reflection/ReflectionPropertiesGetterTest.php +++ /dev/null @@ -1,134 +0,0 @@ -getProperties(ClassWithMixedProperties::class); - - self::assertCount(5, $properties); - - foreach ($properties as $property) { - self::assertInstanceOf('ReflectionProperty', $property); - } - } - - public function testRetrievedInstancesAreNotStatic(): void - { - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - foreach ($properties as $property) { - self::assertFalse($property->isStatic()); - } - } - - public function testExpectedKeys(): void - { - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - self::assertArrayHasKey( - "\0" . ClassWithMixedProperties::class . "\0" . 'privateProperty', - $properties - ); - self::assertArrayHasKey( - "\0" . ClassWithMixedProperties::class . "\0" . 'privatePropertyOverride', - $properties - ); - self::assertArrayHasKey( - "\0" . ParentClass::class . "\0" . 'privatePropertyOverride', - $properties - ); - self::assertArrayHasKey( - "\0*\0protectedProperty", - $properties - ); - self::assertArrayHasKey( - 'publicProperty', - $properties - ); - } - - public function testPropertiesAreAccessible(): void - { - $object = new ClassWithMixedProperties(); - $properties = (new ReflectionPropertiesGetter(new RuntimeReflectionService())) - ->getProperties(ClassWithMixedProperties::class); - - foreach ($properties as $property) { - self::assertSame($property->getName(), $property->getValue($object)); - } - } - - public function testPropertyGetterIsIdempotent(): void - { - $getter = (new ReflectionPropertiesGetter(new RuntimeReflectionService())); - - self::assertSame( - $getter->getProperties(ClassWithMixedProperties::class), - $getter->getProperties(ClassWithMixedProperties::class) - ); - } - - public function testPropertyGetterWillSkipPropertiesNotRetrievedByTheRuntimeReflectionService(): void - { - $reflectionService = $this->createMock(ReflectionService::class); - assert($reflectionService instanceof ReflectionService || $reflectionService instanceof MockObject); - - $reflectionService - ->expects(self::exactly(2)) - ->method('getClass') - ->with(self::logicalOr(ClassWithMixedProperties::class, ParentClass::class)) - ->will(self::returnValueMap([ - [ClassWithMixedProperties::class, new ReflectionClass(ClassWithMixedProperties::class)], - [ParentClass::class, new ReflectionClass(ParentClass::class)], - ])); - - $reflectionService - ->expects(self::atLeastOnce()) - ->method('getAccessibleProperty'); - - $getter = (new ReflectionPropertiesGetter($reflectionService)); - - self::assertEmpty($getter->getProperties(ClassWithMixedProperties::class)); - } - - public function testPropertyGetterWillSkipClassesNotRetrievedByTheRuntimeReflectionService(): void - { - $reflectionService = $this->createMock(ReflectionService::class); - assert($reflectionService instanceof ReflectionService || $reflectionService instanceof MockObject); - - $reflectionService - ->expects(self::once()) - ->method('getClass') - ->with(ClassWithMixedProperties::class); - - $reflectionService->expects(self::never())->method('getAccessibleProperty'); - - $getter = (new ReflectionPropertiesGetter($reflectionService)); - - self::assertEmpty($getter->getProperties(ClassWithMixedProperties::class)); - } -} diff --git a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php index e8b07733d79..84e9f064eb3 100644 --- a/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php @@ -11,7 +11,6 @@ use Doctrine\DBAL\Driver; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\ORM\EntityNotFoundException; -use Doctrine\ORM\Events; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; @@ -23,7 +22,6 @@ use Doctrine\ORM\OptimisticLockException; use Doctrine\ORM\ORMInvalidArgumentException; use Doctrine\ORM\UnitOfWork; -use Doctrine\Persistence\Event\LifecycleEventArgs; use Doctrine\Persistence\NotifyPropertyChanged; use Doctrine\Persistence\PropertyChangedListener; use Doctrine\Tests\Mocks\ConnectionMock; @@ -31,15 +29,12 @@ use Doctrine\Tests\Mocks\EntityPersisterMock; use Doctrine\Tests\Mocks\UnitOfWorkMock; use Doctrine\Tests\Models\CMS\CmsPhonenumber; -use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\Forum\ForumAvatar; use Doctrine\Tests\Models\Forum\ForumUser; use Doctrine\Tests\OrmTestCase; use PHPUnit\Framework\MockObject\MockObject; use stdClass; -use function assert; -use function gc_collect_cycles; use function get_class; use function method_exists; use function random_int; @@ -472,115 +467,6 @@ public function entitiesWithInvalidIdentifiersProvider(): array ]; } - /** - * @group 5689 - * @group 1465 - */ - public function testObjectHashesOfMergedEntitiesAreNotUsedInOriginalEntityDataMap(): void - { - $user = new CmsUser(); - $user->name = 'ocramius'; - $mergedUser = $this->_unitOfWork->merge($user); - - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($user), 'No original data was stored'); - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($mergedUser), 'No original data was stored'); - - $user = null; - $mergedUser = null; - - // force garbage collection of $user (frees the used object hashes, which may be recycled) - gc_collect_cycles(); - - $newUser = new CmsUser(); - $newUser->name = 'ocramius'; - - $this->_unitOfWork->persist($newUser); - - self::assertSame([], $this->_unitOfWork->getOriginalEntityData($newUser), 'No original data was stored'); - } - - /** - * @group DDC-1955 - * @group 5570 - * @group 6174 - */ - public function testMergeWithNewEntityWillPersistItAndTriggerPrePersistListenersWithMergedEntityData(): void - { - $entity = new EntityWithRandomlyGeneratedField(); - - $generatedFieldValue = $entity->generatedField; - - $this - ->eventManager - ->expects(self::any()) - ->method('hasListeners') - ->willReturnCallback(static function ($eventName) { - return $eventName === Events::prePersist; - }); - $this - ->eventManager - ->expects(self::once()) - ->method('dispatchEvent') - ->with( - self::anything(), - self::callback(static function (LifecycleEventArgs $args) use ($entity, $generatedFieldValue) { - $object = $args->getObject(); - assert($object instanceof EntityWithRandomlyGeneratedField); - - self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); - self::assertNotSame($entity, $object); - self::assertSame($generatedFieldValue, $object->generatedField); - - return true; - }) - ); - - $object = $this->_unitOfWork->merge($entity); - assert($object instanceof EntityWithRandomlyGeneratedField); - - self::assertNotSame($object, $entity); - self::assertInstanceOf(EntityWithRandomlyGeneratedField::class, $object); - self::assertSame($object->generatedField, $entity->generatedField); - } - - /** - * @group DDC-1955 - * @group 5570 - * @group 6174 - */ - public function testMergeWithExistingEntityWillNotPersistItNorTriggerPrePersistListeners(): void - { - $persistedEntity = new EntityWithRandomlyGeneratedField(); - $mergedEntity = new EntityWithRandomlyGeneratedField(); - - $mergedEntity->id = $persistedEntity->id; - $mergedEntity->generatedField = random_int( - $persistedEntity->generatedField + 1, - $persistedEntity->generatedField + 1000 - ); - - $this - ->eventManager - ->expects(self::any()) - ->method('hasListeners') - ->willReturnCallback(static function ($eventName) { - return $eventName === Events::prePersist; - }); - $this->eventManager->expects(self::never())->method('dispatchEvent'); - - $this->_unitOfWork->registerManaged( - $persistedEntity, - ['id' => $persistedEntity->id], - ['generatedField' => $persistedEntity->generatedField] - ); - - $merged = $this->_unitOfWork->merge($mergedEntity); - assert($merged instanceof EntityWithRandomlyGeneratedField); - - self::assertSame($merged, $persistedEntity); - self::assertSame($persistedEntity->generatedField, $mergedEntity->generatedField); - } - /** * Unlike next test, this one demonstrates that the problem does * not necessarily reproduce if all the pieces are being flushed together.