diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..c704d7d05b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "CI" + target-branch: "2.19.x" diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index ac2788b39a4..659da17bac6 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -24,4 +24,4 @@ on: jobs: coding-standards: - uses: "doctrine/.github/.github/workflows/coding-standards.yml@3.0.0" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e9facd697de..b84a9422133 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -75,7 +75,7 @@ jobs: if: "${{ matrix.dbal-version != 'default' }}" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: composer-options: "--ignore-platform-req=php+" dependency-versions: "${{ matrix.deps }}" @@ -156,7 +156,7 @@ jobs: if: "${{ matrix.dbal-version != 'default' }}" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: composer-options: "--ignore-platform-req=php+" @@ -222,7 +222,7 @@ jobs: extensions: "${{ matrix.extension }}" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: composer-options: "--ignore-platform-req=php+" @@ -296,7 +296,7 @@ jobs: if: "${{ matrix.dbal-version != 'default' }}" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: composer-options: "--ignore-platform-req=php+" @@ -337,6 +337,8 @@ jobs: path: "reports" - name: "Upload to Codecov" - uses: "codecov/codecov-action@v3" + uses: "codecov/codecov-action@v4" with: directory: reports + env: + CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index ef8053a211c..65cbad613b2 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -36,7 +36,7 @@ jobs: run: "composer require --dev phpdocumentor/guides-cli --no-update" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "highest" diff --git a/.github/workflows/phpbench.yml b/.github/workflows/phpbench.yml index d98e7fa2158..2a09ec3b18f 100644 --- a/.github/workflows/phpbench.yml +++ b/.github/workflows/phpbench.yml @@ -47,15 +47,8 @@ jobs: coverage: "pcov" ini-values: "zend.assertions=1, apc.enable_cli=1" - - name: "Cache dependencies installed with composer" - uses: "actions/cache@v3" - with: - path: "~/.composer/cache" - key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" - restore-keys: "php-${{ matrix.php-version }}-composer-locked-" - - - name: "Install dependencies with composer" - run: "composer update --no-interaction --no-progress" + - name: "Install dependencies with Composer" + uses: "ramsey/composer-install@v3" - name: "Run PHPBench" run: "vendor/bin/phpbench run --report=default" diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index d46dc4c36bb..89d4fe8bf1c 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -7,7 +7,7 @@ on: jobs: release: - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@4.0.0" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5082f75051d..52f30872a9c 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -83,7 +83,7 @@ jobs: if: "${{ matrix.dbal-version != 'default' }}" - name: Install dependencies with Composer - uses: ramsey/composer-install@v2 + uses: ramsey/composer-install@v3 - name: Run static analysis with Vimeo Psalm run: vendor/bin/psalm --shepherd diff --git a/docs/en/cookbook/validation-of-entities.rst b/docs/en/cookbook/validation-of-entities.rst index c2a928dd688..7c44f41c109 100644 --- a/docs/en/cookbook/validation-of-entities.rst +++ b/docs/en/cookbook/validation-of-entities.rst @@ -11,7 +11,7 @@ What we offer are hooks to execute any kind of validation. .. note:: You don't need to validate your entities in the lifecycle - events. Its only one of many options. Of course you can also + events. It is only one of many options. Of course you can also perform validations in value setters or any other method of your entities that are used in your code. diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index bbe2631dfac..fcb2c8a42df 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -870,8 +870,8 @@ This is essentially the same as the following, more verbose, mapping: * @var Collection */ #[JoinTable(name: 'User_Group')] - #[JoinColumn(name: 'User_id', referencedColumnName: 'id')] - #[InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')] + #[JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] #[ManyToMany(targetEntity: Group::class)] private Collection $groups; // ... @@ -884,10 +884,10 @@ This is essentially the same as the following, more verbose, mapping: - + - + diff --git a/src/Internal/Hydration/AbstractHydrator.php b/src/Internal/Hydration/AbstractHydrator.php index 232b9c3ae9a..d8bffe4ad39 100644 --- a/src/Internal/Hydration/AbstractHydrator.php +++ b/src/Internal/Hydration/AbstractHydrator.php @@ -104,29 +104,31 @@ final public function toIterable(Result $stmt, ResultSetMapping $resultSetMappin $this->prepare(); - while (true) { - $row = $this->statement()->fetchAssociative(); - - if ($row === false) { - $this->cleanup(); + try { + while (true) { + $row = $this->statement()->fetchAssociative(); - break; - } + if ($row === false) { + break; + } - $result = []; + $result = []; - $this->hydrateRowData($row, $result); + $this->hydrateRowData($row, $result); - $this->cleanupAfterRowIteration(); - if (count($result) === 1) { - if (count($resultSetMapping->indexByMap) === 0) { - yield end($result); + $this->cleanupAfterRowIteration(); + if (count($result) === 1) { + if (count($resultSetMapping->indexByMap) === 0) { + yield end($result); + } else { + yield from $result; + } } else { - yield from $result; + yield $result; } - } else { - yield $result; } + } finally { + $this->cleanup(); } } diff --git a/src/Query/SqlWalker.php b/src/Query/SqlWalker.php index 018c2455e49..004d29e773c 100644 --- a/src/Query/SqlWalker.php +++ b/src/Query/SqlWalker.php @@ -30,6 +30,7 @@ use function implode; use function is_array; use function is_float; +use function is_int; use function is_numeric; use function is_string; use function preg_match; @@ -384,7 +385,9 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str $values = []; if ($class->discriminatorValue !== null) { // discriminators can be 0 - $values[] = $conn->quote($class->discriminatorValue); + $values[] = $class->getDiscriminatorColumn()->type === 'integer' && is_int($class->discriminatorValue) + ? $class->discriminatorValue + : $conn->quote((string) $class->discriminatorValue); } foreach ($class->subClasses as $subclassName) { @@ -396,7 +399,9 @@ private function generateDiscriminatorColumnConditionSQL(array $dqlAliases): str continue; } - $values[] = $conn->quote((string) $subclassMetadata->discriminatorValue); + $values[] = $subclassMetadata->getDiscriminatorColumn()->type === 'integer' && is_int($subclassMetadata->discriminatorValue) + ? $subclassMetadata->discriminatorValue + : $conn->quote((string) $subclassMetadata->discriminatorValue); } if ($values !== []) { @@ -2246,8 +2251,10 @@ private function getChildDiscriminatorsFromClassMetadata( $discriminators += HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($metadata, $this->em); } - foreach (array_keys($discriminators) as $dis) { - $sqlParameterList[] = $this->conn->quote($dis); + foreach (array_keys($discriminators) as $discriminatorValue) { + $sqlParameterList[] = $rootClass->getDiscriminatorColumn()->type === 'integer' && is_int($discriminatorValue) + ? $discriminatorValue + : $this->conn->quote((string) $discriminatorValue); } return '(' . implode(', ', $sqlParameterList) . ')'; diff --git a/src/UnitOfWork.php b/src/UnitOfWork.php index b07bf8aa518..d1659de272b 100644 --- a/src/UnitOfWork.php +++ b/src/UnitOfWork.php @@ -1143,6 +1143,8 @@ private function executeDeletions(): void $eventsToDispatch = []; foreach ($entities as $entity) { + $this->removeFromIdentityMap($entity); + $oid = spl_object_id($entity); $class = $this->em->getClassMetadata($entity::class); $persister = $this->getEntityPersister($class->name); @@ -1484,8 +1486,6 @@ public function scheduleForDelete(object $entity): void return; } - $this->removeFromIdentityMap($entity); - unset($this->entityUpdates[$oid]); if (! isset($this->entityDeletions[$oid])) { @@ -2653,7 +2653,7 @@ private function eagerLoadCollections(array $collections, ToManyInverseSideMappi $entities[] = $collection->getOwner(); } - $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities]); + $found = $this->getEntityPersister($targetEntity)->loadAll([$mappedBy => $entities], $mapping['orderBy'] ?? null); $targetClass = $this->em->getClassMetadata($targetEntity); $targetProperty = $targetClass->getReflectionProperty($mappedBy); diff --git a/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php b/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php index 5a6aa5da3ab..fcc82cf35c6 100644 --- a/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php +++ b/tests/Tests/ORM/Functional/QueryDqlFunctionTest.php @@ -252,7 +252,6 @@ public function testOperatorMultiply(): void self::assertEquals(1_600_000, $result[3]['op']); } - #[Group('test')] public function testOperatorDiv(): void { $result = $this->_em->createQuery('SELECT m, (m.salary/0.5) AS op FROM Doctrine\Tests\Models\Company\CompanyManager m ORDER BY m.salary ASC') diff --git a/tests/Tests/ORM/Functional/Ticket/GH11163Test.php b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php new file mode 100644 index 00000000000..d4e75389c19 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11163Test.php @@ -0,0 +1,112 @@ +setUpEntitySchema([ + GH11163Bucket::class, + GH11163BucketItem::class, + ]); + } + + public function tearDown(): void + { + parent::tearDown(); + + $conn = static::$sharedConn; + $conn->executeStatement('DELETE FROM GH11163BucketItem'); + $conn->executeStatement('DELETE FROM GH11163Bucket'); + } + + public function testFetchEagerModeWithOrderBy(): void + { + // Load entities into database + $this->_em->persist($bucket = new GH11163Bucket(11163)); + $this->_em->persist(new GH11163BucketItem(1, $bucket, 2)); + $this->_em->persist(new GH11163BucketItem(2, $bucket, 3)); + $this->_em->persist(new GH11163BucketItem(3, $bucket, 1)); + $this->_em->flush(); + $this->_em->clear(); + + // Fetch entity from database + $dql = 'SELECT bucket FROM ' . GH11163Bucket::class . ' bucket WHERE bucket.id = :id'; + $bucket = $this->_em->createQuery($dql) + ->setParameter('id', 11163) + ->getSingleResult(); + + // Assert associated entity is loaded eagerly + static::assertInstanceOf(GH11163Bucket::class, $bucket); + static::assertInstanceOf(PersistentCollection::class, $bucket->items); + static::assertTrue($bucket->items->isInitialized()); + + static::assertCount(3, $bucket->items); + + // Assert order of entities + static::assertSame(1, $bucket->items[0]->position); + static::assertSame(3, $bucket->items[0]->id); + + static::assertSame(2, $bucket->items[1]->position); + static::assertSame(1, $bucket->items[1]->id); + + static::assertSame(3, $bucket->items[2]->position); + static::assertSame(2, $bucket->items[2]->id); + } +} + +#[ORM\Entity] +class GH11163Bucket +{ + #[ORM\Id] + #[ORM\Column(type: 'integer')] + private int $id; + + /** @var Collection */ + #[ORM\OneToMany( + targetEntity: GH11163BucketItem::class, + mappedBy: 'bucket', + fetch: 'EAGER', + )] + #[ORM\OrderBy(['position' => 'ASC'])] + public Collection $items; + + public function __construct(int $id) + { + $this->id = $id; + $this->items = new ArrayCollection(); + } +} + +#[ORM\Entity] +class GH11163BucketItem +{ + #[ORM\ManyToOne(targetEntity: GH11163Bucket::class, inversedBy: 'items')] + #[ORM\JoinColumn(nullable: false)] + private GH11163Bucket $bucket; + + #[ORM\Id] + #[ORM\Column(type: 'integer')] + public int $id; + + #[ORM\Column(type: 'integer')] + public int $position; + + public function __construct(int $id, GH11163Bucket $bucket, int $position) + { + $this->id = $id; + $this->bucket = $bucket; + $this->position = $position; + } +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH11341Test.php b/tests/Tests/ORM/Functional/Ticket/GH11341Test.php new file mode 100644 index 00000000000..5c35dfe86c3 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH11341Test.php @@ -0,0 +1,157 @@ +setUpEntitySchema([ + IntegerBaseClass::class, + IntegerFooEntity::class, + IntegerBarEntity::class, + StringAsIntBaseClass::class, + StringAsIntFooEntity::class, + StringAsIntBarEntity::class, + StringBaseClass::class, + StringFooEntity::class, + StringBarEntity::class, + ]); + } + + public static function dqlStatements(): Generator + { + yield ['SELECT e FROM ' . IntegerBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(1, 2\)$/']; + yield ['SELECT e FROM ' . IntegerFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(1\)$/']; + yield ['SELECT e FROM ' . IntegerBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(2\)$/']; + yield ['SELECT e FROM ' . StringAsIntBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\', \'2\'\)$/']; + yield ['SELECT e FROM ' . StringAsIntFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\'\)$/']; + yield ['SELECT e FROM ' . StringAsIntBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'2\'\)$/']; + yield ['SELECT e FROM ' . StringBaseClass::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\', \'2\'\)$/']; + yield ['SELECT e FROM ' . StringFooEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'1\'\)$/']; + yield ['SELECT e FROM ' . StringBarEntity::class . ' e', '/WHERE [a-z]0_.type IN \(\'2\'\)$/']; + } + + #[DataProvider('dqlStatements')] + public function testDiscriminatorValue(string $dql, string $expectedDiscriminatorValues): void + { + $query = $this->_em->createQuery($dql); + $sql = $query->getSQL(); + + self::assertMatchesRegularExpression($expectedDiscriminatorValues, $sql); + } + + public static function dqlStatementsForInstanceOf(): Generator + { + yield [IntegerBaseClass::class, IntegerFooEntity::class]; + yield [StringBaseClass::class, StringFooEntity::class]; + yield [StringAsIntBaseClass::class, StringAsIntFooEntity::class]; + } + + /** + * @psalm-param class-string $baseClass + * @psalm-param class-string $inheritedClass + */ + #[DataProvider('dqlStatementsForInstanceOf')] + public function testInstanceOf(string $baseClass, string $inheritedClass): void + { + $this->_em->persist(new $inheritedClass()); + $this->_em->flush(); + + $dql = 'SELECT p FROM ' . $baseClass . ' p WHERE p INSTANCE OF ' . $baseClass; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + self::assertCount(1, $result); + self::assertContainsOnlyInstancesOf($baseClass, $result); + } +} + +#[ORM\Entity] +#[ORM\Table(name: 'integer_discriminator')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'integer')] +#[ORM\DiscriminatorMap([ + 1 => IntegerFooEntity::class, + 2 => IntegerBarEntity::class, +])] +class IntegerBaseClass +{ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; +} + +#[ORM\Entity] +class IntegerFooEntity extends IntegerBaseClass +{ +} + +#[ORM\Entity] +class IntegerBarEntity extends IntegerBaseClass +{ +} + +#[ORM\Entity] +#[ORM\Table(name: 'string_as_int_discriminator')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap([ + 1 => StringAsIntFooEntity::class, + 2 => StringAsIntBarEntity::class, +])] +class StringAsIntBaseClass +{ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; +} + +#[ORM\Entity] +class StringAsIntFooEntity extends StringAsIntBaseClass +{ +} + +#[ORM\Entity] +class StringAsIntBarEntity extends StringAsIntBaseClass +{ +} + + +#[ORM\Entity] +#[ORM\Table(name: 'string_discriminator')] +#[ORM\InheritanceType('SINGLE_TABLE')] +#[ORM\DiscriminatorColumn(name: 'type', type: 'string')] +#[ORM\DiscriminatorMap([ + '1' => StringFooEntity::class, + '2' => StringBarEntity::class, +])] +class StringBaseClass +{ + #[ORM\Id] + #[ORM\GeneratedValue(strategy: 'IDENTITY')] + #[ORM\Column(type: 'integer')] + private int|null $id = null; +} + +#[ORM\Entity] +class StringFooEntity extends StringBaseClass +{ +} + +#[ORM\Entity] +class StringBarEntity extends StringBaseClass +{ +} diff --git a/tests/Tests/ORM/Functional/Ticket/GH6123Test.php b/tests/Tests/ORM/Functional/Ticket/GH6123Test.php new file mode 100644 index 00000000000..60bea99a7e6 --- /dev/null +++ b/tests/Tests/ORM/Functional/Ticket/GH6123Test.php @@ -0,0 +1,79 @@ +createSchemaForModels( + GH6123Entity::class, + ); + } + + public function testLoadingRemovedEntityFromDatabaseDoesNotCreateNewManagedEntityInstance(): void + { + $entity = new GH6123Entity(); + $this->_em->persist($entity); + $this->_em->flush(); + + self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity)); + self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity)); + + $this->_em->remove($entity); + + $freshEntity = $this->loadEntityFromDatabase($entity->id); + self::assertSame($entity, $freshEntity); + + self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($freshEntity)); + self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($freshEntity)); + } + + public function testRemovedEntityCanBePersistedAgain(): void + { + $entity = new GH6123Entity(); + $this->_em->persist($entity); + $this->_em->flush(); + + $this->_em->remove($entity); + self::assertSame(UnitOfWork::STATE_REMOVED, $this->_em->getUnitOfWork()->getEntityState($entity)); + self::assertTrue($this->_em->getUnitOfWork()->isScheduledForDelete($entity)); + + $this->loadEntityFromDatabase($entity->id); + + $this->_em->persist($entity); + self::assertSame(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($entity)); + self::assertFalse($this->_em->getUnitOfWork()->isScheduledForDelete($entity)); + + $this->_em->flush(); + } + + private function loadEntityFromDatabase(int $id): GH6123Entity|null + { + return $this->_em->createQueryBuilder() + ->select('e') + ->from(GH6123Entity::class, 'e') + ->where('e.id = :id') + ->setParameter('id', $id) + ->getQuery() + ->getOneOrNullResult(); + } +} + +#[ORM\Entity] +class GH6123Entity +{ + #[ORM\Id] + #[ORM\GeneratedValue] + #[ORM\Column(type: Types::INTEGER)] + public int $id; +} diff --git a/tests/Tests/ORM/Hydration/AbstractHydratorTest.php b/tests/Tests/ORM/Hydration/AbstractHydratorTest.php index a9b451e1c19..d83ee1a8647 100644 --- a/tests/Tests/ORM/Hydration/AbstractHydratorTest.php +++ b/tests/Tests/ORM/Hydration/AbstractHydratorTest.php @@ -13,6 +13,7 @@ use Doctrine\ORM\Exception\ORMException; use Doctrine\ORM\Internal\Hydration\AbstractHydrator; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\Tests\Models\Hydration\SimpleEntity; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; @@ -149,4 +150,33 @@ public function testHydrateAllClearsAllAttachedListenersEvenOnError(): void $this->expectException(ORMException::class); $this->hydrator->hydrateAll($this->mockResult, $this->mockResultMapping); } + + public function testToIterableIfYieldAndBreakBeforeFinishAlwaysCleansUp(): void + { + $this->setUpEntitySchema([SimpleEntity::class]); + + $entity1 = new SimpleEntity(); + $this->_em->persist($entity1); + $entity2 = new SimpleEntity(); + $this->_em->persist($entity2); + + $this->_em->flush(); + $this->_em->clear(); + + $evm = $this->_em->getEventManager(); + + $q = $this->_em->createQuery('SELECT e.id FROM ' . SimpleEntity::class . ' e'); + + // select two entities, but do no iterate + $q->toIterable(); + self::assertCount(0, $evm->getListeners(Events::onClear)); + + // select two entities, but abort after first record + foreach ($q->toIterable() as $result) { + self::assertCount(1, $evm->getListeners(Events::onClear)); + break; + } + + self::assertCount(0, $evm->getListeners(Events::onClear)); + } } diff --git a/tests/Tests/ORM/UnitOfWorkTest.php b/tests/Tests/ORM/UnitOfWorkTest.php index 550b1cfe1c8..a9112fa5d51 100644 --- a/tests/Tests/ORM/UnitOfWorkTest.php +++ b/tests/Tests/ORM/UnitOfWorkTest.php @@ -288,12 +288,18 @@ public function testRemovedAndRePersistedEntitiesAreInTheIdentityMapAndAreNotGar $entity->id = 123; $this->_unitOfWork->registerManaged($entity, ['id' => 123], []); + self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity)); + self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity)); self::assertTrue($this->_unitOfWork->isInIdentityMap($entity)); $this->_unitOfWork->remove($entity); - self::assertFalse($this->_unitOfWork->isInIdentityMap($entity)); + self::assertSame(UnitOfWork::STATE_REMOVED, $this->_unitOfWork->getEntityState($entity)); + self::assertTrue($this->_unitOfWork->isScheduledForDelete($entity)); + self::assertTrue($this->_unitOfWork->isInIdentityMap($entity)); $this->_unitOfWork->persist($entity); + self::assertSame(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($entity)); + self::assertFalse($this->_unitOfWork->isScheduledForDelete($entity)); self::assertTrue($this->_unitOfWork->isInIdentityMap($entity)); }