From cc3d872b958587c17df5b40b437d23ae03890a82 Mon Sep 17 00:00:00 2001 From: Thibault Buathier Date: Mon, 5 Jun 2023 10:58:32 +0200 Subject: [PATCH 01/19] revert: transform backed enum to value --- lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php index 144f340d00..623c2cb61c 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -273,10 +273,6 @@ public function executeInserts() $paramIndex = 1; foreach ($insertData[$tableName] as $column => $value) { - if ($value instanceof BackedEnum) { - $value = $value->value; - } - $stmt->bindValue($paramIndex++, $value, $this->columnTypes[$column]); } } From 6c9b29f237c0f56792bbf4e00f6180fb1ede5bfe Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 5 Jun 2023 21:42:13 +0200 Subject: [PATCH 02/19] Don't call canEmulateSchemas in SchemaTool when possible (#10762) --- lib/Doctrine/ORM/Tools/SchemaTool.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index 6f9a4193d4..dc0fbc53e7 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -12,6 +12,7 @@ use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Schema\Visitor\RemoveNamespacedAssets; use Doctrine\Deprecations\Deprecation; @@ -406,8 +407,14 @@ static function (ClassMetadata $class) use ($idMapping): bool { } } - if (! $this->platform->supportsSchemas() && ! $this->platform->canEmulateSchemas()) { - $schema->visit(new RemoveNamespacedAssets()); + if (! $this->platform->supportsSchemas()) { + $filter = /** @param Sequence|Table $asset */ static function ($asset) use ($schema): bool { + return ! $asset->isInDefaultNamespace($schema->getName()); + }; + + if (array_filter($schema->getSequences() + $schema->getTables(), $filter) && ! $this->platform->canEmulateSchemas()) { + $schema->visit(new RemoveNamespacedAssets()); + } } if ($eventManager->hasListeners(ToolEvents::postGenerateSchema)) { From 3827dd769edf25d59fa29a7d63efd6ba15b3a789 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 6 Jun 2023 11:27:31 +0200 Subject: [PATCH 03/19] Don't call deprecated getSQLResultCasing and usesSequenceEmulatedIdentityColumns when we know the platform (#10759) --- lib/Doctrine/ORM/Internal/SQLResultCasing.php | 4 +++- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 7 ++++++- phpstan-dbal2.neon | 3 +++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Internal/SQLResultCasing.php b/lib/Doctrine/ORM/Internal/SQLResultCasing.php index 3edfc91196..d82a37ba61 100644 --- a/lib/Doctrine/ORM/Internal/SQLResultCasing.php +++ b/lib/Doctrine/ORM/Internal/SQLResultCasing.php @@ -9,7 +9,9 @@ use Doctrine\DBAL\Platforms\OraclePlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use function get_class; use function method_exists; +use function strpos; use function strtolower; use function strtoupper; @@ -26,7 +28,7 @@ private function getSQLResultCasing(AbstractPlatform $platform, string $column): return strtolower($column); } - if (method_exists(AbstractPlatform::class, 'getSQLResultCasing')) { + if (strpos(get_class($platform), 'Doctrine\\DBAL\\Platforms\\') !== 0 && method_exists(AbstractPlatform::class, 'getSQLResultCasing')) { return $platform->getSQLResultCasing($column); } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index f7aec6e3ba..3d97dce814 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -7,6 +7,9 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Platforms; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLServerPlatform; use Doctrine\Deprecations\Deprecation; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; @@ -621,9 +624,11 @@ private function completeIdGeneratorMapping(ClassMetadataInfo $class): void case ClassMetadata::GENERATOR_TYPE_IDENTITY: $sequenceName = null; $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; + $platform = $this->getTargetPlatform(); // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. - if ($this->getTargetPlatform()->usesSequenceEmulatedIdentityColumns()) { + /** @psalm-suppress UndefinedClass, InvalidClass */ + if (! $platform instanceof MySQLPlatform && ! $platform instanceof SqlitePlatform && ! $platform instanceof SQLServerPlatform && $platform->usesSequenceEmulatedIdentityColumns()) { Deprecation::trigger( 'doctrine/orm', 'https://github.com/doctrine/orm/issues/8850', diff --git a/phpstan-dbal2.neon b/phpstan-dbal2.neon index 440358bc41..646aceadc5 100644 --- a/phpstan-dbal2.neon +++ b/phpstan-dbal2.neon @@ -10,6 +10,9 @@ parameters: - '/Call to an undefined method Doctrine\\DBAL\\Connection::createSchemaManager\(\)\./' # Class name will change in DBAL 3. - '/^Class Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform not found\.$/' + - + message: '/Doctrine\\DBAL\\Platforms\\MyS(ql|QL)Platform/' + path: lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php # Forward compatibility for DBAL 3.5 - '/^Call to an undefined method Doctrine\\DBAL\\Platforms\\AbstractPlatform::getAlterSchemaSQL\(\).$/' From 33675ff4a96c1b4b51e119bd95a5fb03d9518a14 Mon Sep 17 00:00:00 2001 From: Michael Roterman Date: Tue, 6 Jun 2023 18:18:26 +0200 Subject: [PATCH 04/19] fix: Update baseline and assertions for OneToManyPersister --- .../ORM/Persisters/Collection/OneToManyPersister.php | 9 ++++++++- psalm-baseline.xml | 8 -------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php index db5024d257..70e195e6ec 100644 --- a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php @@ -16,6 +16,7 @@ use function array_values; use function assert; use function implode; +use function is_int; use function is_string; /** @@ -183,7 +184,11 @@ private function deleteEntityCollection(PersistentCollection $collection): int $statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; - return $this->conn->executeStatement($statement, $parameters); + $numAffected = $this->conn->executeStatement($statement, $parameters); + + assert(is_int($numAffected)); + + return $numAffected; } /** @@ -247,6 +252,8 @@ private function deleteJoinedEntityCollection(PersistentCollection $collection): $this->conn->executeStatement($statement); + assert(is_int($numDeleted)); + return $numDeleted; } } diff --git a/psalm-baseline.xml b/psalm-baseline.xml index b480604632..aab77a75ed 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1236,14 +1236,6 @@ $mapping['indexBy'] => $index, ]]]> - - $numDeleted - conn->executeStatement($statement, $parameters)]]> - - - int - int - getOwner()]]> getOwner()]]> From 3c0d140e525747c2bcecb8686fc62b5b78e916b0 Mon Sep 17 00:00:00 2001 From: Michael Roterman Date: Tue, 6 Jun 2023 18:19:20 +0200 Subject: [PATCH 05/19] OneToManyPersister does not take custom identifier types into account for orphan removal. In my case a custom doctrine type of Uuid object is converted to string by simply casting it, resulting in a hex DELETE FROM x WHERE id = ? query, whilst it should've been converted along the way to it's binary representation. This leads to no deletions being made at all as you would expect making use of doctrine custom type's as an identifier. This commit fixes usage of ramsey/uuid or symfony/uid as custom id types when making use of orphan removal. --- .../Collection/OneToManyPersister.php | 4 +- .../Tests/ORM/Functional/GH10747Test.php | 195 ++++++++++++++++++ 2 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/GH10747Test.php diff --git a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php index 70e195e6ec..f39415fc0c 100644 --- a/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/Collection/OneToManyPersister.php @@ -175,16 +175,18 @@ private function deleteEntityCollection(PersistentCollection $collection): int $targetClass = $this->em->getClassMetadata($mapping['targetEntity']); $columns = []; $parameters = []; + $types = []; foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] as $joinColumn) { $columns[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $targetClass, $this->platform); $parameters[] = $identifier[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]; + $types[] = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $sourceClass, $this->em); } $statement = 'DELETE FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; - $numAffected = $this->conn->executeStatement($statement, $parameters); + $numAffected = $this->conn->executeStatement($statement, $parameters, $types); assert(is_int($numAffected)); diff --git a/tests/Doctrine/Tests/ORM/Functional/GH10747Test.php b/tests/Doctrine/Tests/ORM/Functional/GH10747Test.php new file mode 100644 index 0000000000..019449a416 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/GH10747Test.php @@ -0,0 +1,195 @@ +setUpEntitySchema([GH10747Article::class, GH10747Credit::class]); + } + + public function testOrphanedOneToManyDeletesCollection(): void + { + $object = new GH10747Article( + new CustomIdObject('article') + ); + + $creditOne = new GH10747Credit( + $object, + 'credit1' + ); + + $creditTwo = new GH10747Credit( + $object, + 'credit2' + ); + + $object->setCredits(new ArrayCollection([$creditOne, $creditTwo])); + + $this->_em->persist($object); + $this->_em->persist($creditOne); + $this->_em->persist($creditTwo); + $this->_em->flush(); + + $id = $object->id; + + $object2 = $this->_em->find(GH10747Article::class, $id); + + $creditThree = new GH10747Credit( + $object2, + 'credit3' + ); + + $object2->setCredits(new ArrayCollection([$creditThree])); + + $this->_em->persist($object2); + $this->_em->persist($creditThree); + $this->_em->flush(); + + $currentDatabaseCredits = $this->_em->createQueryBuilder() + ->select('c.id') + ->from(GH10747Credit::class, 'c') + ->getQuery() + ->execute(); + + self::assertCount(1, $currentDatabaseCredits); + } +} + +/** + * @Entity + * @Table + */ +class GH10747Article +{ + /** + * @Id + * @Column(type="Doctrine\Tests\ORM\Functional\GH10747CustomIdObjectHashType") + * @var CustomIdObject + */ + public $id; + + /** + * @ORM\OneToMany(targetEntity="GH10747Credit", mappedBy="article", orphanRemoval=true) + * + * @var Collection + */ + public $credits; + + public function __construct(CustomIdObject $id) + { + $this->id = $id; + $this->credits = new ArrayCollection(); + } + + public function setCredits(Collection $credits): void + { + $this->credits = $credits; + } + + /** @return Collection */ + public function getCredits(): Collection + { + return $this->credits; + } +} + +/** + * @Entity + * @Table + */ +class GH10747Credit +{ + /** + * @ORM\Column(type="integer") + * @ORM\GeneratedValue() + * + * @Id() + * @var int|null + */ + public $id = null; + + /** @var string */ + public $name; + + /** + * @ORM\ManyToOne(targetEntity="GH10747Article", inversedBy="credits") + * + * @var GH10747Article + */ + public $article; + + public function __construct(GH10747Article $article, string $name) + { + $this->article = $article; + $this->name = $name; + } +} + +class GH10747CustomIdObjectHashType extends DBALType +{ + /** + * {@inheritDoc} + */ + public function convertToDatabaseValue($value, AbstractPlatform $platform) + { + return $value->id . '_test'; + } + + /** + * {@inheritDoc} + */ + public function convertToPHPValue($value, AbstractPlatform $platform) + { + return new CustomIdObject(str_replace('_test', '', $value)); + } + + /** + * {@inheritDoc} + */ + public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform) + { + if (method_exists($platform, 'getStringTypeDeclarationSQL')) { + return $platform->getStringTypeDeclarationSQL($fieldDeclaration); + } + + return $platform->getVarcharTypeDeclarationSQL($fieldDeclaration); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return self::class; + } +} From cbf45dd97ebe518bd90614af0ea489d63dc60797 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 8 Jun 2023 16:57:07 +0200 Subject: [PATCH 06/19] PHPStan 1.10.18, Psalm 5.12.0 (#10771) --- composer.json | 4 ++-- psalm-baseline.xml | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index f8a72fc661..0955d56aef 100644 --- a/composer.json +++ b/composer.json @@ -42,14 +42,14 @@ "doctrine/annotations": "^1.13 || ^2", "doctrine/coding-standard": "^9.0.2 || ^12.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.14", + "phpstan/phpstan": "~1.4.10 || 1.10.18", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^4.4 || ^5.4 || ^6.0", "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.11.0" + "vimeo/psalm": "4.30.0 || 5.12.0" }, "conflict": { "doctrine/annotations": "<1.13 || >= 3.0" diff --git a/psalm-baseline.xml b/psalm-baseline.xml index b480604632..2a93fc8465 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + IterableResult @@ -213,6 +213,11 @@ CacheProvider + + + directory, self::LOCK_EXTENSION)]]> + + (int) $defaultLifetime @@ -437,6 +442,9 @@ $baseElement + + resultPointers[$parent]]]> + resultPointers[$parent][key($first)]]]> resultPointers[$dqlAlias] =& $coll[key($coll)]]]> @@ -2456,6 +2464,9 @@ + + + From c3106f9fe714d9bd0ed5b423b4f4ffd7e8d787aa Mon Sep 17 00:00:00 2001 From: Vaidas Date: Fri, 9 Jun 2023 09:54:22 +0300 Subject: [PATCH 07/19] Restore document proxy state to uninitialized on load exception (#10645) --- lib/Doctrine/ORM/Proxy/ProxyFactory.php | 13 ++++++++- .../Tests/ORM/Proxy/ProxyFactoryTest.php | 28 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index fe19c7d71e..2ba41caba7 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -21,6 +21,7 @@ use ReflectionProperty; use Symfony\Component\VarExporter\ProxyHelper; use Symfony\Component\VarExporter\VarExporter; +use Throwable; use function array_flip; use function str_replace; @@ -204,7 +205,17 @@ private function createInitializer(ClassMetadata $classMetadata, EntityPersister $identifier = $classMetadata->getIdentifierValues($proxy); - if ($entityPersister->loadById($identifier, $proxy) === null) { + try { + $entity = $entityPersister->loadById($identifier, $proxy); + } catch (Throwable $exception) { + $proxy->__setInitializer($initializer); + $proxy->__setCloner($cloner); + $proxy->__setInitialized(false); + + throw $exception; + } + + if ($entity === null) { $proxy->__setInitializer($initializer); $proxy->__setCloner($cloner); $proxy->__setInitialized(false); diff --git a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php index 8342058a70..804cbbe040 100644 --- a/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php +++ b/tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php @@ -21,6 +21,7 @@ use Doctrine\Tests\Models\ECommerce\ECommerceFeature; use Doctrine\Tests\OrmTestCase; use Doctrine\Tests\PHPUnitCompatibility\MockBuilderCompatibilityTools; +use Exception; use ReflectionProperty; use stdClass; @@ -145,6 +146,33 @@ public function testFailedProxyLoadingDoesNotMarkTheProxyAsInitialized(): void self::assertFalse($proxy->__isInitialized()); } + public function testExceptionOnProxyLoadingDoesNotMarkTheProxyAsInitialized(): void + { + $persister = $this + ->getMockBuilderWithOnlyMethods(BasicEntityPersister::class, ['load', 'getClassMetadata']) + ->disableOriginalConstructor() + ->getMock(); + $this->uowMock->setEntityPersister(ECommerceFeature::class, $persister); + + $proxy = $this->proxyFactory->getProxy(ECommerceFeature::class, ['id' => 42]); + assert($proxy instanceof Proxy); + + $exception = new Exception('Literally any kind of connection exception'); + + $persister + ->expects(self::atLeastOnce()) + ->method('load') + ->will(self::throwException($exception)); + + try { + $proxy->getDescription(); + self::fail('An exception was expected to be raised'); + } catch (Exception $exception) { + } + + self::assertFalse($proxy->__isInitialized(), 'The proxy should not be initialized'); + } + /** @group DDC-2432 */ public function testFailedProxyCloningDoesNotMarkTheProxyAsInitialized(): void { From e5174af669fee1e118a6fd75a2566529f888aeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 11 Jun 2023 15:33:33 +0200 Subject: [PATCH 08/19] Document how to produce DTOs with a result set mapping --- docs/en/reference/native-sql.rst | 34 +++++++++ .../Tests/ORM/Functional/NativeQueryTest.php | 72 +++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/docs/en/reference/native-sql.rst b/docs/en/reference/native-sql.rst index 434423bf5b..97d92dd386 100644 --- a/docs/en/reference/native-sql.rst +++ b/docs/en/reference/native-sql.rst @@ -250,6 +250,40 @@ The first parameter is the name of the column in the SQL result set and the second parameter is the result alias under which the value of the column will be placed in the transformed Doctrine result. +Special case: DTOs +................... + +You can also use ``ResultSetMapping`` to map the results of a native SQL +query to a DTO (Data Transfer Object). This is done by adding scalar +results for each argument of the DTO's constructor, then filling the +``newObjectMappings`` property of the ``ResultSetMapping`` with +information about where to map each scalar result: + +.. code-block:: php + + addScalarResult('name', 1, 'string'); + $rsm->addScalarResult('email', 2, 'string'); + $rsm->addScalarResult('city', 3, 'string'); + $rsm->newObjectMappings['name'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, // a result can contain many DTOs, this is the index of the DTO to map to + 'argIndex' => 0, // each scalar result can be mapped to a different argument of the DTO constructor + ]; + $rsm->newObjectMappings['email'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 1, + ]; + $rsm->newObjectMappings['city'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 2, + ]; + + Meta results ~~~~~~~~~~~~ diff --git a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php index 8597b2f088..521393e969 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php @@ -19,6 +19,7 @@ use Doctrine\Tests\Models\CMS\CmsEmail; use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsUser; +use Doctrine\Tests\Models\CMS\CmsUserDTO; use Doctrine\Tests\Models\Company\CompanyContract; use Doctrine\Tests\Models\Company\CompanyEmployee; use Doctrine\Tests\Models\Company\CompanyFixContract; @@ -155,6 +156,77 @@ public function testJoinedOneToManyNativeQuery(): void self::assertSame($phones[0]->getUser(), $users[0]); } + public function testMappingAsDto(): void + { + $user = new CmsUser(); + $user->name = 'Roman'; + $user->username = 'romanb'; + $user->status = 'dev'; + + $phone = new CmsPhonenumber(); + $phone->phonenumber = 424242; + + $user->addPhonenumber($phone); + + $email = new CmsEmail(); + $email->email = 'fabio.bat.silva@gmail.com'; + + $user->setEmail($email); + + $addr = new CmsAddress(); + $addr->country = 'germany'; + $addr->zip = 10827; + $addr->city = 'Berlin'; + + $user->setAddress($addr); + + $this->_em->persist($user); + $this->_em->flush(); + + $this->_em->clear(); + + $rsm = new ResultSetMapping(); + $rsm->addScalarResult('name', 1, 'string'); + $rsm->addScalarResult('email', 2, 'string'); + $rsm->addScalarResult('city', 3, 'string'); + $rsm->newObjectMappings['name'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 0, + ]; + $rsm->newObjectMappings['email'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 1, + ]; + $rsm->newObjectMappings['city'] = [ + 'className' => CmsUserDTO::class, + 'objIndex' => 0, + 'argIndex' => 2, + ]; + $query = $this->_em->createNativeQuery( + <<<'SQL' + SELECT u.name, e.email, a.city + FROM cms_users u +INNER JOIN cms_phonenumbers p ON u.id = p.user_id +INNER JOIN cms_emails e ON e.id = u.email_id +INNER JOIN cms_addresses a ON u.id = a.user_id + WHERE username = ? +SQL + , + $rsm + ); + $query->setParameter(1, 'romanb'); + + $users = $query->getResult(); + self::assertCount(1, $users); + $user = $users[0]; + self::assertInstanceOf(CmsUserDTO::class, $user); + self::assertEquals('Roman', $user->name); + self::assertEquals('fabio.bat.silva@gmail.com', $user->email); + self::assertEquals('Berlin', $user->address); + } + public function testJoinedOneToOneNativeQuery(): void { $user = new CmsUser(); From 5132f0deb0e7becd0c2075d59c4e8f350db1a8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 18 Jun 2023 14:11:25 +0200 Subject: [PATCH 09/19] Stop using $message argument It brings nothing over what PHPUnit now natively does. --- tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 6ed3aa7eab..0235323287 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -32,7 +32,6 @@ use function class_exists; use function get_class; -use function sprintf; // DBAL 2 compatibility class_exists('Doctrine\DBAL\Platforms\MySqlPlatform'); @@ -74,8 +73,7 @@ public function assertSqlGeneration( parent::assertEquals( $sqlToBeConfirmed, - $sqlGenerated, - sprintf('"%s" is not equal to "%s"', $sqlGenerated, $sqlToBeConfirmed) + $sqlGenerated ); $query->free(); From fcc5c106b4489c6ddf0fe0fd106479c5ea2316f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 18 Jun 2023 14:14:38 +0200 Subject: [PATCH 10/19] Rely on partial objects less when in tests Partial objects are deprecated. They were handy to make the generated SQL more legible, but we should refrain from relying on them. --- .../ORM/Query/SelectSqlGenerationTest.php | 260 ++++++++---------- 1 file changed, 119 insertions(+), 141 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 0235323287..37986fa9c9 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -62,8 +62,7 @@ public function assertSqlGeneration( $query->setParameter($name, $value); } - $query->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true) - ->useQueryCache(false); + $query->useQueryCache(false); foreach ($queryHints as $name => $value) { $query->setHint($name, $value); @@ -96,8 +95,7 @@ public function assertInvalidSqlGeneration( $query->setParameter($name, $value); } - $query->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true) - ->useQueryCache(false); + $query->useQueryCache(false); foreach ($queryHints as $name => $value) { $query->setHint($name, $value); @@ -115,8 +113,7 @@ public function testJoinWithRangeVariablePutsConditionIntoSqlWhereClause(): void { $this->assertSqlGeneration( 'SELECT c.id FROM Doctrine\Tests\Models\Company\CompanyPerson c JOIN Doctrine\Tests\Models\Company\CompanyPerson r WHERE c.spouse = r AND r.id = 42', - 'SELECT c0_.id AS id_0 FROM company_persons c0_ INNER JOIN company_persons c1_ WHERE c0_.spouse_id = c1_.id AND c1_.id = 42', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => true] + 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42' ); } @@ -130,8 +127,7 @@ public function testJoinWithRangeVariableAndInheritancePutsConditionIntoSqlWhere */ $this->assertSqlGeneration( 'SELECT c.id FROM Doctrine\Tests\Models\Company\CompanyPerson c JOIN Doctrine\Tests\Models\Company\CompanyPerson r WHERE c.spouse = r AND r.id = 42', - 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + 'SELECT c0_.id AS id_0 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id WHERE c0_.spouse_id = c3_.id AND c3_.id = 42' ); } @@ -139,7 +135,7 @@ public function testSupportsSelectForAllFields(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_' ); } @@ -163,7 +159,7 @@ public function testSupportsSelectForAllNestedField(): void { $this->assertSqlGeneration( 'SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u ORDER BY u.name ASC', - 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3 FROM cms_articles c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id ORDER BY c1_.name ASC' + 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id ORDER BY c1_.name ASC' ); } @@ -171,7 +167,7 @@ public function testNotExistsExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NOT EXISTS (SELECT p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE NOT EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NOT EXISTS (SELECT c1_.phonenumber FROM cms_phonenumbers c1_ WHERE c1_.phonenumber = 1234)' ); } @@ -187,7 +183,7 @@ public function testSupportsSelectUsingMultipleFromComponents(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id' ); } @@ -195,7 +191,7 @@ public function testSupportsJoinOnMultipleComponents(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsPhonenumber p WITH u = p.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)' ); } @@ -203,17 +199,17 @@ public function testSupportsJoinOnMultipleComponentsWithJoinedInheritanceType(): { $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN (company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id) ON (c0_.id = c3_.id)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)' ); $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyManager m WITH e.id = m.id', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN (company_managers c2_ INNER JOIN company_employees c4_ ON c2_.id = c4_.id INNER JOIN company_persons c3_ ON c2_.id = c3_.id) ON (c0_.id = c3_.id)' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id LEFT JOIN (company_managers c3_ INNER JOIN company_employees c5_ ON c3_.id = c5_.id INNER JOIN company_persons c4_ ON c3_.id = c4_.id) ON (c0_.id = c4_.id)' ); $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s LEFT JOIN Doctrine\Tests\Models\Company\CompanyEvent e WITH s.id = e.id', - 'SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_events c3_ ON (c2_.id = c3_.id) WHERE c0_.discr IN (\'fix\', \'flexible\', \'flexultra\')' + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN (company_events c4_ LEFT JOIN company_auctions c5_ ON c4_.id = c5_.id LEFT JOIN company_raffles c6_ ON c4_.id = c6_.id) ON (c2_.id = c4_.id) WHERE c0_.discr IN ('fix', 'flexible', 'flexultra')" ); } @@ -221,7 +217,7 @@ public function testSupportsSelectWithCollectionAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.phonenumber AS phonenumber_4, c0_.email_id AS email_id_5, c1_.user_id AS user_id_6 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON c0_.id = c1_.user_id' ); } @@ -229,7 +225,7 @@ public function testSupportsSelectWithSingleValuedAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\Forum\ForumUser u JOIN u.avatar a', - 'SELECT f0_.id AS id_0, f0_.username AS username_1, f1_.id AS id_2 FROM forum_users f0_ INNER JOIN forum_avatars f1_ ON f0_.avatar_id = f1_.id' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f1_.id AS id_2, f0_.avatar_id AS avatar_id_3 FROM forum_users f0_ INNER JOIN forum_avatars f1_ ON f0_.avatar_id = f1_.id' ); } @@ -273,7 +269,7 @@ public function testSupportsOrderByWithAscAsDefault(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ ORDER BY f0_.id ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC' ); } @@ -281,7 +277,7 @@ public function testSupportsOrderByAsc(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id asc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ ORDER BY f0_.id ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id ASC' ); } @@ -289,7 +285,7 @@ public function testSupportsOrderByDesc(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY u.id desc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ ORDER BY f0_.id DESC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY f0_.id DESC' ); } @@ -297,7 +293,7 @@ public function testSupportsOrderByWithSimpleArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u ORDER BY LENGTH(u.username) + LENGTH(u.username) asc', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ ORDER BY LENGTH(f0_.username) + LENGTH(f0_.username) ASC' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ ORDER BY LENGTH(f0_.username) + LENGTH(f0_.username) ASC' ); } @@ -345,7 +341,7 @@ public function testSupportsWhereClauseWithPositionalParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.id = ?1', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id = ?' ); } @@ -353,7 +349,7 @@ public function testSupportsWhereClauseWithNamedParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.username = :name', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.username = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ?' ); } @@ -361,7 +357,7 @@ public function testSupportsWhereAndClauseWithNamedParameters(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where u.username = :name and u.username = :name2', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.username = ? AND f0_.username = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username = ? AND f0_.username = ?' ); } @@ -369,7 +365,7 @@ public function testSupportsCombinedWhereClauseWithNamedParameter(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\Forum\ForumUser u where (u.username = :name OR u.username = :name2) AND u.id = :id', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE (f0_.username = ? OR f0_.username = ?) AND f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE (f0_.username = ? OR f0_.username = ?) AND f0_.id = ?' ); } @@ -394,7 +390,7 @@ public function testSupportsArithmeticExpressionsInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE ((u.id + 5000) * u.id + 3) < 10000000', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + 5000) * c0_.id + 3) < 10000000' ); } @@ -402,7 +398,7 @@ public function testSupportsMultipleEntitiesInFromClause(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u2 WHERE u.id = u2.id', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id' ); } @@ -410,7 +406,7 @@ public function testSupportsMultipleEntitiesInFromClauseUsingPathExpression(): v { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.id = a.user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c1_.id AS id_4, c1_.topic AS topic_5, c1_.text AS text_6, c1_.version AS version_7, c0_.email_id AS email_id_8, c1_.user_id AS user_id_9 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id' ); } @@ -431,11 +427,11 @@ public function testSupportsJoinAndWithClauseRestriction(): void { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE '%foo%'", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a WITH a.topic LIKE '%foo%'", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ INNER JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE '%foo%')" ); } @@ -509,7 +505,7 @@ public function testSupportsInstanceOfExpressionsInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" ); } @@ -518,7 +514,7 @@ public function testSupportsInstanceOfExpressionInWherePartWithMultipleValues(): // This also uses FQCNs starting with or without a backslash in the INSTANCE OF parameter $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF (Doctrine\Tests\Models\Company\CompanyEmployee, \Doctrine\Tests\Models\Company\CompanyManager)', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" ); } @@ -527,7 +523,7 @@ public function testSupportsInstanceOfExpressionsInWherePartPrefixedSlash(): voi { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF \Doctrine\Tests\Models\Company\CompanyEmployee', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN ('manager', 'employee')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN ('manager', 'employee')" ); } @@ -544,7 +540,7 @@ public function testSupportsInstanceOfExpressionsInWherePartInDeeperLevel(): voi { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c0_.discr AS discr_5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id WHERE c0_.discr IN ('manager')" ); } @@ -552,7 +548,7 @@ public function testSupportsInstanceOfExpressionsInWherePartInDeepestLevel(): vo { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyManager u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c2_.car_id AS car_id_8 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')" ); } @@ -560,7 +556,7 @@ public function testSupportsInstanceOfExpressionsUsingInputParameterInWherePart( { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF ?1', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.discr AS discr_2 FROM company_persons c0_ WHERE c0_.discr IN (?)', + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c0_.discr AS discr_6, c0_.spouse_id AS spouse_id_7, c1_.car_id AS car_id_8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE c0_.discr IN (?)', [], [1 => $this->entityManager->getClassMetadata(CompanyEmployee::class)] ); @@ -579,7 +575,7 @@ public function testSupportsMultipleValuedInExpressionInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1, 2)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.id IN (1, 2)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id IN (1, 2)' ); } @@ -587,7 +583,7 @@ public function testSupportsNotInExpressionInWherePart(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :id NOT IN (1)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE ? NOT IN (1)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ? NOT IN (1)' ); } @@ -604,7 +600,7 @@ public function testInExpressionWithSingleValuedAssociationPathExpressionInWhere { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)' ); } @@ -623,11 +619,11 @@ public function testInExpressionWithArithmeticExpression(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.username IN (FOO('Lo'), 'Lo', :name)", - "SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)" + "SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)" ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.id IN (1 + 1)', - 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)' + 'SELECT f0_.id AS id_0, f0_.username AS username_1, f0_.avatar_id AS avatar_id_2 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)' ); } @@ -672,7 +668,7 @@ public function testSubqueriesInComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE (u.id >= (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = :name)) AND (u.id <= (SELECT u3.id FROM Doctrine\Tests\Models\CMS\CmsUser u3 WHERE u3.name = :name))', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (c0_.id >= (SELECT c1_.id FROM cms_users c1_ WHERE c1_.name = ?)) AND (c0_.id <= (SELECT c2_.id FROM cms_users c2_ WHERE c2_.name = ?))' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id >= (SELECT c1_.id FROM cms_users c1_ WHERE c1_.name = ?)) AND (c0_.id <= (SELECT c2_.id FROM cms_users c2_ WHERE c2_.name = ?))' ); } @@ -680,7 +676,6 @@ public function testSupportsMemberOfExpressionOneToMany(): void { // "Get all users who have $phone as a phonenumber." (*cough* doesnt really make sense...) $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); $phone = new CmsPhonenumber(); $phone->phonenumber = 101; @@ -696,7 +691,6 @@ public function testSupportsMemberOfExpressionManyToMany(): void { // "Get all users who are members of $group." $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.groups'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); $group = new CmsGroup(); $group->id = 101; @@ -711,7 +705,6 @@ public function testSupportsMemberOfExpressionManyToMany(): void public function testSupportsMemberOfExpressionManyToManyParameterArray(): void { $q = $this->entityManager->createQuery('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.groups'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); $group = new CmsGroup(); $group->id = 101; @@ -764,21 +757,18 @@ public function testSupportsMemberOfWithIdentificationVariable(): void public function testSupportsCurrentDateFunction(): void { $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_date()'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_datetime > CURRENT_DATE', $q->getSql()); } public function testSupportsCurrentTimeFunction(): void { $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.time > current_time()'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_time > CURRENT_TIME', $q->getSql()); } public function testSupportsCurrentTimestampFunction(): void { $q = $this->entityManager->createQuery('SELECT d.id FROM Doctrine\Tests\Models\Generic\DateTimeModel d WHERE d.datetime > current_timestamp()'); - $q->setHint(ORMQuery::HINT_FORCE_PARTIAL_LOAD, true); self::assertEquals('SELECT d0_.id AS id_0 FROM date_time_model d0_ WHERE d0_.col_datetime > CURRENT_TIMESTAMP', $q->getSql()); } @@ -793,10 +783,7 @@ public function testExistsExpressionInWhereCorrelatedSubqueryAssocCondition(): v FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp WHERE spouseEmp = emp.spouse)', // SQL - 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1 FROM cms_employees c0_' - . ' WHERE EXISTS (' - . 'SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id' - . ')' + 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT c1_.id FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)' ); } @@ -811,10 +798,7 @@ public function testExistsExpressionWithSimpleSelectReturningScalar(): void FROM Doctrine\Tests\Models\CMS\CmsEmployee spouseEmp WHERE spouseEmp = emp.spouse)', // SQL - 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1 FROM cms_employees c0_' - . ' WHERE EXISTS (' - . 'SELECT 1 AS sclr_2 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id' - . ')' + 'SELECT DISTINCT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ WHERE EXISTS (SELECT 1 AS sclr_3 FROM cms_employees c1_ WHERE c1_.id = c0_.spouse_id)' ); } @@ -848,7 +832,7 @@ public function testSizeFunction(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1' ); } @@ -856,7 +840,7 @@ public function testSizeFunctionSupportsManyToMany(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.groups) > 1', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id) > 1' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_users_groups c1_ WHERE c1_.user_id = c0_.id) > 1' ); } @@ -864,11 +848,11 @@ public function testEmptyCollectionComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS NOT EMPTY', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (SELECT COUNT(*) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0' ); } @@ -876,7 +860,7 @@ public function testNestedExpressions(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where u.id > 10 and u.id < 42 and ((u.id * 2) > 5)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id > 10 AND c0_.id < 42 AND ((c0_.id * 2) > 5)' ); } @@ -884,7 +868,7 @@ public function testNestedExpressions2(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where (u.id > 10) and (u.id < 42 and ((u.id * 2) > 5)) or u.id <> 42', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id < 42 AND ((c0_.id * 2) > 5)) OR c0_.id <> 42' ); } @@ -892,7 +876,7 @@ public function testNestedExpressions3(): void { $this->assertSqlGeneration( 'select u from Doctrine\Tests\Models\CMS\CmsUser u where (u.id > 10) and (u.id between 1 and 10 or u.id in (1, 2, 3, 4, 5))', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id BETWEEN 1 AND 10 OR c0_.id IN (1, 2, 3, 4, 5))' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id > 10) AND (c0_.id BETWEEN 1 AND 10 OR c0_.id IN (1, 2, 3, 4, 5))' ); } @@ -900,7 +884,7 @@ public function testOrderByCollectionAssociationSize(): void { $this->assertSqlGeneration( 'select u, size(u.articles) as numArticles from Doctrine\Tests\Models\CMS\CmsUser u order by numArticles', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT COUNT(*) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr_4 FROM cms_users c0_ ORDER BY sclr_4 ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT COUNT(*) FROM cms_articles c1_ WHERE c1_.user_id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ ORDER BY sclr_4 ASC' ); } @@ -908,7 +892,7 @@ public function testOrderBySupportsSingleValuedPathExpressionOwningSide(): void { $this->assertSqlGeneration( 'select a from Doctrine\Tests\Models\CMS\CmsArticle a order by a.user', - 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3 FROM cms_articles c0_ ORDER BY c0_.user_id ASC' + 'SELECT c0_.id AS id_0, c0_.topic AS topic_1, c0_.text AS text_2, c0_.version AS version_3, c0_.user_id AS user_id_4 FROM cms_articles c0_ ORDER BY c0_.user_id ASC' ); } @@ -959,7 +943,7 @@ public function testSingleValuedAssociationFieldInWhere(): void { $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1', - 'SELECT c0_.phonenumber AS phonenumber_0 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?' + 'SELECT c0_.phonenumber AS phonenumber_0, c0_.user_id AS user_id_1 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?' ); } @@ -967,7 +951,7 @@ public function testSingleValuedAssociationNullCheckOnOwningSide(): void { $this->assertSqlGeneration( 'SELECT a FROM Doctrine\Tests\Models\CMS\CmsAddress a WHERE a.user IS NULL', - 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3 FROM cms_addresses c0_ WHERE c0_.user_id IS NULL' + 'SELECT c0_.id AS id_0, c0_.country AS country_1, c0_.zip AS zip_2, c0_.city AS city_3, c0_.user_id AS user_id_4 FROM cms_addresses c0_ WHERE c0_.user_id IS NULL' ); } @@ -980,7 +964,7 @@ public function testSingleValuedAssociationNullCheckOnInverseSide(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.address a WHERE a.id IS NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL' ); } @@ -1014,7 +998,7 @@ public function testStringFunctionLikeExpression(): void ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic LIKE u.name', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE c0_.name)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic LIKE c0_.name)' ); } @@ -1032,7 +1016,7 @@ public function testStringFunctionNotLikeExpression(): void ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic NOT LIKE u.name', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)' ); } @@ -1041,8 +1025,7 @@ public function testOrderedCollectionFetchJoined(): void { $this->assertSqlGeneration( 'SELECT r, l FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.legs l', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.departureDate AS departureDate_2, r1_.arrivalDate AS arrivalDate_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteLegs r2_ ON r0_.id = r2_.route_id INNER JOIN RoutingLeg r1_ ON r1_.id = r2_.leg_id ' . - 'ORDER BY r1_.departureDate ASC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.departureDate AS departureDate_2, r1_.arrivalDate AS arrivalDate_3, r1_.from_id AS from_id_4, r1_.to_id AS to_id_5 FROM RoutingRoute r0_ INNER JOIN RoutingRouteLegs r2_ ON r0_.id = r2_.route_id INNER JOIN RoutingLeg r1_ ON r1_.id = r2_.leg_id ORDER BY r1_.departureDate ASC' ); } @@ -1066,8 +1049,7 @@ public function testPessimisticWriteLockQueryHint(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 ' . - "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_WRITE] ); } @@ -1082,8 +1064,7 @@ public function testPessimisticReadLockQueryHintPostgreSql(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 ' . - "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE", + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR SHARE", [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] ); } @@ -1096,8 +1077,7 @@ public function testLockModeNoneQueryHint(): void { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 ' . - "FROM cms_users c0_ WHERE c0_.username = 'gblanco'", + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco'", [ORMQuery::HINT_LOCK_MODE => LockMode::NONE] ); } @@ -1107,7 +1087,7 @@ public function testSupportSelectWithMoreThan10InputParameters(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1 OR u.id = ?2 OR u.id = ?3 OR u.id = ?4 OR u.id = ?5 OR u.id = ?6 OR u.id = ?7 OR u.id = ?8 OR u.id = ?9 OR u.id = ?10 OR u.id = ?11', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ? OR c0_.id = ?' ); } @@ -1121,8 +1101,7 @@ public function testPessimisticReadLockQueryHintMySql(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 ' . - "FROM cms_users c0_ WHERE c0_.username = 'gblanco' LOCK IN SHARE MODE", + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' LOCK IN SHARE MODE", [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] ); } @@ -1137,8 +1116,7 @@ public function testPessimisticReadLockQueryHintOracle(): void $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.username = 'gblanco'", - 'SELECT c0_.id AS ID_0, c0_.status AS STATUS_1, c0_.username AS USERNAME_2, c0_.name AS NAME_3 ' . - "FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", + "SELECT c0_.id AS ID_0, c0_.status AS STATUS_1, c0_.username AS USERNAME_2, c0_.name AS NAME_3, c0_.email_id AS EMAIL_ID_4 FROM cms_users c0_ WHERE c0_.username = 'gblanco' FOR UPDATE", [ORMQuery::HINT_LOCK_MODE => LockMode::PESSIMISTIC_READ] ); } @@ -1162,7 +1140,7 @@ public function testMappedSuperclassAssociationJoin(): void { $this->assertSqlGeneration( 'SELECT f FROM Doctrine\Tests\Models\DirectoryTree\File f JOIN f.parentDirectory d WHERE f.id = ?1', - 'SELECT f0_.id AS id_0, f0_.extension AS extension_1, f0_.name AS name_2 FROM "file" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' + 'SELECT f0_.id AS id_0, f0_.extension AS extension_1, f0_.name AS name_2, f0_.parentDirectory_id AS parentDirectory_id_3 FROM "file" f0_ INNER JOIN Directory d1_ ON f0_.parentDirectory_id = d1_.id WHERE f0_.id = ?' ); } @@ -1230,7 +1208,7 @@ public function testSubSelectAliasesFromOuterQuery(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_' ); } @@ -1238,7 +1216,7 @@ public function testSubSelectAliasesFromOuterQueryWithSubquery(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id AND ui.name IN (SELECT uii.name FROM Doctrine\Tests\Models\CMS\CmsUser uii)) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id AND c1_.name IN (SELECT c2_.name FROM cms_users c2_)) AS sclr_4 FROM cms_users c0_' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id AND c1_.name IN (SELECT c2_.name FROM cms_users c2_)) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_' ); } @@ -1246,7 +1224,7 @@ public function testSubSelectAliasesFromOuterQueryReuseInWhereClause(): void { $this->assertSqlGeneration( 'SELECT uo, (SELECT ui.name FROM Doctrine\Tests\Models\CMS\CmsUser ui WHERE ui.id = uo.id) AS bar FROM Doctrine\Tests\Models\CMS\CmsUser uo WHERE bar = ?0', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4 FROM cms_users c0_ WHERE sclr_4 = ?' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, (SELECT c1_.name FROM cms_users c1_ WHERE c1_.id = c0_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ WHERE sclr_4 = ?' ); } @@ -1412,13 +1390,13 @@ public function testIdentityFunctionInJoinedSubclass(): void //relation is in the subclass (CompanyManager) we are querying $this->assertSqlGeneration( 'SELECT m, IDENTITY(m.car) as car_id FROM Doctrine\Tests\Models\Company\CompanyManager m', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c2_.car_id AS sclr_6, c0_.discr AS discr_7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c2_.car_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' ); //relation is in the base class (CompanyPerson). $this->assertSqlGeneration( 'SELECT m, IDENTITY(m.spouse) as spouse_id FROM Doctrine\Tests\Models\Company\CompanyManager m', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.spouse_id AS sclr_6, c0_.discr AS discr_7 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, c0_.spouse_id AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id' ); } @@ -1556,8 +1534,7 @@ public function testSelfReferenceWithOneToOneDoesNotDuplicateAlias(): void { $this->assertSqlGeneration( 'SELECT p, pp FROM Doctrine\Tests\Models\Company\CompanyPerson p JOIN p.spouse pp', - 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c3_.id AS id_6, c3_.name AS name_7, c4_.title AS title_8, c5_.salary AS salary_9, c5_.department AS department_10, c5_.startDate AS startDate_11, c0_.discr AS discr_12, c0_.spouse_id AS spouse_id_13, c1_.car_id AS car_id_14, c3_.discr AS discr_15, c3_.spouse_id AS spouse_id_16, c4_.car_id AS car_id_17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.title AS title_2, c2_.salary AS salary_3, c2_.department AS department_4, c2_.startDate AS startDate_5, c3_.id AS id_6, c3_.name AS name_7, c4_.title AS title_8, c5_.salary AS salary_9, c5_.department AS department_10, c5_.startDate AS startDate_11, c0_.discr AS discr_12, c0_.spouse_id AS spouse_id_13, c1_.car_id AS car_id_14, c3_.discr AS discr_15, c3_.spouse_id AS spouse_id_16, c4_.car_id AS car_id_17 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id INNER JOIN company_persons c3_ ON c0_.spouse_id = c3_.id LEFT JOIN company_managers c4_ ON c3_.id = c4_.id LEFT JOIN company_employees c5_ ON c3_.id = c5_.id' ); } @@ -1578,7 +1555,7 @@ public function testIssue331(): void { $this->assertSqlGeneration( 'SELECT e.name FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id' + 'SELECT c0_.name AS name_0 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id' ); } @@ -1610,12 +1587,12 @@ public function testGroupByAllFieldsWhenObjectHasForeignKeys(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY u', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id' ); $this->assertSqlGeneration( 'SELECT e FROM Doctrine\Tests\Models\CMS\CmsEmployee e GROUP BY e', - 'SELECT c0_.id AS id_0, c0_.name AS name_1 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id' + 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.spouse_id AS spouse_id_2 FROM cms_employees c0_ GROUP BY c0_.id, c0_.name, c0_.spouse_id' ); } @@ -1624,7 +1601,7 @@ public function testGroupBySupportsResultVariable(): void { $this->assertSqlGeneration( 'SELECT u, u.status AS st FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY st', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.status AS status_4 FROM cms_users c0_ GROUP BY c0_.status' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.status AS status_4, c0_.email_id AS email_id_5 FROM cms_users c0_ GROUP BY c0_.status' ); } @@ -1633,7 +1610,7 @@ public function testGroupBySupportsIdentificationVariable(): void { $this->assertSqlGeneration( 'SELECT u AS user FROM Doctrine\Tests\Models\CMS\CmsUser u GROUP BY user', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ GROUP BY id_0, status_1, username_2, name_3' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ GROUP BY id_0, status_1, username_2, name_3' ); } @@ -1642,7 +1619,7 @@ public function testSupportsBitComparison(): void { $this->assertSqlGeneration( 'SELECT BIT_OR(4,2), BIT_AND(4,2), u FROM Doctrine\Tests\Models\CMS\CmsUser u', - 'SELECT (4 | 2) AS sclr_0, (4 & 2) AS sclr_1, c0_.id AS id_2, c0_.status AS status_3, c0_.username AS username_4, c0_.name AS name_5 FROM cms_users c0_' + 'SELECT (4 | 2) AS sclr_0, (4 & 2) AS sclr_1, c0_.id AS id_2, c0_.status AS status_3, c0_.username AS username_4, c0_.name AS name_5, c0_.email_id AS email_id_6 FROM cms_users c0_' ); $this->assertSqlGeneration( 'SELECT BIT_OR(u.id,2), BIT_AND(u.id,2) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE BIT_OR(u.id,2) > 0', @@ -1663,15 +1640,15 @@ public function testParenthesesOnTheLeftHandOfComparison(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where ( (u.id + u.id) * u.id ) > 100', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE ((c0_.id + c0_.id) * c0_.id) > 100' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE ((c0_.id + c0_.id) * c0_.id) > 100' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where (u.id + u.id) * u.id > 100', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE (c0_.id + c0_.id) * c0_.id > 100' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE (c0_.id + c0_.id) * c0_.id > 100' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u where 100 < (u.id + u.id) * u.id ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE 100 < (c0_.id + c0_.id) * c0_.id' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE 100 < (c0_.id + c0_.id) * c0_.id' ); } @@ -1688,27 +1665,27 @@ public function testSupportsSubSqlFunction(): void { $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_4 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_)' ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.name IN ( SELECT TRIM(u2.name) FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE LOWER(u2.name) LIKE \'%fabio%\')', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_4 FROM cms_users c1_ WHERE LOWER(c1_.name) LIKE \'%fabio%\')' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.name IN (SELECT TRIM(c1_.name) AS sclr_5 FROM cms_users c1_ WHERE LOWER(c1_.name) LIKE \'%fabio%\')' ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.email IN ( SELECT TRIM(IDENTITY(u2.email)) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT TRIM(c1_.email_id) AS sclr_4 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT TRIM(c1_.email_id) AS sclr_5 FROM cms_users c1_)' ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE u1.email IN ( SELECT IDENTITY(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT c1_.email_id AS sclr_4 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IN (SELECT c1_.email_id AS sclr_5 FROM cms_users c1_)' ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) = ( SELECT SUM(u2.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_4 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) = (SELECT SUM(c1_.id) AS sclr_5 FROM cms_users c1_)' ); $this->assertSqlGeneration( 'SELECT u1 FROM Doctrine\Tests\Models\CMS\CmsUser u1 WHERE COUNT(u1.id) <= ( SELECT SUM(u2.id) + COUNT(u2.email) FROM Doctrine\Tests\Models\CMS\CmsUser u2 )', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COUNT(c0_.id) <= (SELECT SUM(c1_.id) + COUNT(c1_.email_id) AS sclr_4 FROM cms_users c1_)' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COUNT(c0_.id) <= (SELECT SUM(c1_.id) + COUNT(c1_.email_id) AS sclr_5 FROM cms_users c1_)' ); } @@ -1751,37 +1728,37 @@ public function testWhereFunctionIsNullComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IS NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.email_id IS NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NULL' ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NULL AND IDENTITY(u.email) IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NULL AND c0_.email_id IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NULL AND c0_.email_id IS NOT NULL" ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE IDENTITY(u.email) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE c0_.email_id IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE c0_.email_id IS NOT NULL' ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE NULLIF(u.name, 'FabioBatSilva') IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE NULLIF(c0_.name, 'FabioBatSilva') IS NOT NULL" ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(u.name, u.id) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COALESCE(c0_.name, c0_.id) IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.name, c0_.id) IS NOT NULL' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(u.id, IDENTITY(u.email)) IS NOT NULL', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COALESCE(c0_.id, c0_.email_id) IS NOT NULL' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.id, c0_.email_id) IS NOT NULL' ); $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE COALESCE(IDENTITY(u.email), NULLIF(u.name, 'FabioBatSilva')) IS NOT NULL", - "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ WHERE COALESCE(c0_.email_id, NULLIF(c0_.name, 'FabioBatSilva')) IS NOT NULL" + "SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ WHERE COALESCE(c0_.email_id, NULLIF(c0_.name, 'FabioBatSilva')) IS NOT NULL" ); } @@ -1823,7 +1800,7 @@ public function testCustomTypeValueSqlForAllFields(): void $this->assertSqlGeneration( 'SELECT p FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p', - 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1 FROM customtype_parents c0_' + 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_' ); } @@ -1837,7 +1814,7 @@ public function testCustomTypeValueSqlForPartialObject(): void $this->assertSqlGeneration( 'SELECT partial p.{id, customInteger} FROM Doctrine\Tests\Models\CustomType\CustomTypeParent p', - 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1 FROM customtype_parents c0_' + 'SELECT c0_.id AS id_0, -(c0_.customInteger) AS customInteger_1, c0_.child_id AS child_id_2 FROM customtype_parents c0_' ); } @@ -1846,7 +1823,7 @@ public function testMultipleFromAndInheritanceCondition(): void { $this->assertSqlGeneration( 'SELECT fix, flex FROM Doctrine\Tests\Models\Company\CompanyFixContract fix, Doctrine\Tests\Models\Company\CompanyFlexContract flex', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c1_.id AS id_3, c1_.completed AS completed_4, c1_.hoursWorked AS hoursWorked_5, c1_.pricePerHour AS pricePerHour_6, c1_.maxPrice AS maxPrice_7, c0_.discr AS discr_8, c1_.discr AS discr_9 FROM company_contracts c0_, company_contracts c1_ WHERE (c0_.discr IN ('fix') AND c1_.discr IN ('flexible', 'flexultra'))" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c1_.id AS id_3, c1_.completed AS completed_4, c1_.hoursWorked AS hoursWorked_5, c1_.pricePerHour AS pricePerHour_6, c1_.maxPrice AS maxPrice_7, c0_.discr AS discr_8, c0_.salesPerson_id AS salesPerson_id_9, c1_.discr AS discr_10, c1_.salesPerson_id AS salesPerson_id_11 FROM company_contracts c0_, company_contracts c1_ WHERE (c0_.discr IN ('fix') AND c1_.discr IN ('flexible', 'flexultra'))" ); } @@ -1855,15 +1832,15 @@ public function testOrderByClauseSupportsSimpleArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.id + 1 ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ ORDER BY c0_.id + 1 ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.id + 1 ASC' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY ( ( (u.id + 1) * (u.id - 1) ) / 2)', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ ORDER BY (((c0_.id + 1) * (c0_.id - 1)) / 2) ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY (((c0_.id + 1) * (c0_.id - 1)) / 2) ASC' ); $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY ((u.id + 5000) * u.id + 3) ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ ORDER BY ((c0_.id + 5000) * c0_.id + 3) ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY ((c0_.id + 5000) * c0_.id + 3) ASC' ); } @@ -1871,7 +1848,7 @@ public function testOrderByClauseSupportsFunction(): void { $this->assertSqlGeneration( 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY CONCAT(u.username, u.name) ', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3 FROM cms_users c0_ ORDER BY c0_.username || c0_.name ASC' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, c0_.email_id AS email_id_4 FROM cms_users c0_ ORDER BY c0_.username || c0_.name ASC' ); } @@ -1927,32 +1904,34 @@ public function testQuotedWalkJoinVariableDeclaration(): void { $this->assertSqlGeneration( 'SELECT u, a FROM Doctrine\Tests\Models\Quote\User u JOIN u.address a', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."address-id" AS addressid_2, q1_."address-zip" AS addresszip_3, q1_.type AS type_4 FROM "quote-user" q0_ INNER JOIN "quote-address" q1_ ON q0_."user-id" = q1_."user-id" AND q1_.type IN (\'simple\', \'full\')' + <<<'SQL' +SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."address-id" AS addressid_2, q1_."address-zip" AS addresszip_3, q1_.type AS type_4, q1_."user-id" AS userid_5, q1_."city-id" AS cityid_6 FROM "quote-user" q0_ INNER JOIN "quote-address" q1_ ON q0_."user-id" = q1_."user-id" AND q1_.type IN ('simple', 'full') +SQL ); $this->assertSqlGeneration( 'SELECT u, p FROM Doctrine\Tests\Models\Quote\User u JOIN u.phones p', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."phone-number" AS phonenumber_2 FROM "quote-user" q0_ INNER JOIN "quote-phone" q1_ ON q0_."user-id" = q1_."user-id"' + 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."phone-number" AS phonenumber_2, q1_."user-id" AS userid_3 FROM "quote-user" q0_ INNER JOIN "quote-phone" q1_ ON q0_."user-id" = q1_."user-id"' ); $this->assertSqlGeneration( 'SELECT u, g FROM Doctrine\Tests\Models\Quote\User u JOIN u.groups g', - 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3 FROM "quote-user" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."user-id" = q2_."user-id" INNER JOIN "quote-group" q1_ ON q1_."group-id" = q2_."group-id"' + 'SELECT q0_."user-id" AS userid_0, q0_."user-name" AS username_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q1_."parent-id" AS parentid_4 FROM "quote-user" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."user-id" = q2_."user-id" INNER JOIN "quote-group" q1_ ON q1_."group-id" = q2_."group-id"' ); $this->assertSqlGeneration( 'SELECT a, u FROM Doctrine\Tests\Models\Quote\Address a JOIN a.user u', - 'SELECT q0_."address-id" AS addressid_0, q0_."address-zip" AS addresszip_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_.type AS type_4 FROM "quote-address" q0_ INNER JOIN "quote-user" q1_ ON q0_."user-id" = q1_."user-id" WHERE q0_.type IN (\'simple\', \'full\')' + 'SELECT q0_."address-id" AS addressid_0, q0_."address-zip" AS addresszip_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_.type AS type_4, q0_."user-id" AS userid_5, q0_."city-id" AS cityid_6 FROM "quote-address" q0_ INNER JOIN "quote-user" q1_ ON q0_."user-id" = q1_."user-id" WHERE q0_.type IN (\'simple\', \'full\')' ); $this->assertSqlGeneration( 'SELECT g, u FROM Doctrine\Tests\Models\Quote\Group g JOIN g.users u', - 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3 FROM "quote-group" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."group-id" = q2_."group-id" INNER JOIN "quote-user" q1_ ON q1_."user-id" = q2_."user-id"' + 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."user-id" AS userid_2, q1_."user-name" AS username_3, q0_."parent-id" AS parentid_4 FROM "quote-group" q0_ INNER JOIN "quote-users-groups" q2_ ON q0_."group-id" = q2_."group-id" INNER JOIN "quote-user" q1_ ON q1_."user-id" = q2_."user-id"' ); $this->assertSqlGeneration( 'SELECT g, p FROM Doctrine\Tests\Models\Quote\Group g JOIN g.parent p', - 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3 FROM "quote-group" q0_ INNER JOIN "quote-group" q1_ ON q0_."parent-id" = q1_."group-id"' + 'SELECT q0_."group-id" AS groupid_0, q0_."group-name" AS groupname_1, q1_."group-id" AS groupid_2, q1_."group-name" AS groupname_3, q0_."parent-id" AS parentid_4, q1_."parent-id" AS parentid_5 FROM "quote-group" q0_ INNER JOIN "quote-group" q1_ ON q0_."parent-id" = q1_."group-id"' ); } @@ -1961,17 +1940,17 @@ public function testCaseThenParameterArithmeticExpression(): void { $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE 0 END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' ); $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN e.salary - :value WHEN e.salary >= :value THEN :value - e.salary ELSE e.salary + 0 END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE c0_.salary + 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN c0_.salary - ? WHEN c0_.salary >= ? THEN ? - c0_.salary ELSE c0_.salary + 0 END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' ); $this->assertSqlGeneration( 'SELECT SUM(CASE WHEN e.salary <= :value THEN (e.salary - :value) WHEN e.salary >= :value THEN (:value - e.salary) ELSE (e.salary + :value) END) FROM Doctrine\Tests\Models\Company\CompanyEmployee e', - 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN (c0_.salary - ?) WHEN c0_.salary >= ? THEN (? - c0_.salary) ELSE (c0_.salary + ?) END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id' + 'SELECT SUM(CASE WHEN c0_.salary <= ? THEN (c0_.salary - ?) WHEN c0_.salary >= ? THEN (? - c0_.salary) ELSE (c0_.salary + ?) END) AS sclr_0 FROM company_employees c0_ INNER JOIN company_persons c1_ ON c0_.id = c1_.id LEFT JOIN company_managers c2_ ON c0_.id = c2_.id' ); } @@ -2037,12 +2016,12 @@ public function testOrderByClauseShouldReplaceOrderByRelationMapping(): void { $this->assertSqlGeneration( 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName ASC' ); $this->assertSqlGeneration( 'SELECT r, b FROM Doctrine\Tests\Models\Routing\RoutingRoute r JOIN r.bookings b ORDER BY b.passengerName DESC', - 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC' + 'SELECT r0_.id AS id_0, r1_.id AS id_1, r1_.passengerName AS passengerName_2, r1_.route_id AS route_id_3 FROM RoutingRoute r0_ INNER JOIN RoutingRouteBooking r1_ ON r0_.id = r1_.route_id ORDER BY r1_.passengerName DESC' ); } @@ -2065,8 +2044,7 @@ public function testClassTableInheritanceJoinWithConditionAppliesToBaseTable(): { $this->assertSqlGeneration( 'SELECT e.id FROM Doctrine\Tests\Models\Company\CompanyOrganization o JOIN o.events e WITH e.id = ?1', - 'SELECT c0_.id AS id_0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)', - [ORMQuery::HINT_FORCE_PARTIAL_LOAD => false] + 'SELECT c0_.id AS id_0 FROM company_organizations c1_ INNER JOIN (company_events c0_ LEFT JOIN company_auctions c2_ ON c0_.id = c2_.id LEFT JOIN company_raffles c3_ ON c0_.id = c3_.id) ON c1_.id = c0_.org_id AND (c0_.id = ?)' ); } @@ -2076,7 +2054,7 @@ public function testSingleTableInheritanceLeftJoinWithCondition(): void // Regression test for the bug $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" ); } @@ -2086,7 +2064,7 @@ public function testSingleTableInheritanceLeftJoinWithConditionAndWhere(): void // Ensure other WHERE predicates are passed through to the main WHERE clause $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e LEFT JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id WHERE e.salary > 1000', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id LEFT JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.salary > 1000" ); } @@ -2096,7 +2074,7 @@ public function testSingleTableInheritanceInnerJoinWithCondition(): void // Test inner joins too $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyEmployee e INNER JOIN Doctrine\Tests\Models\Company\CompanyContract c WITH c.salesPerson = e.id', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id INNER JOIN company_contracts c0_ ON (c0_.salesPerson_id = c2_.id) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" ); } @@ -2107,7 +2085,7 @@ public function testSingleTableInheritanceLeftJoinNonAssociationWithConditionAnd // the where clause when not joining onto that table $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c LEFT JOIN Doctrine\Tests\Models\Company\CompanyEmployee e WITH e.id = c.salesPerson WHERE c.completed = true', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ LEFT JOIN (company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id) ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ LEFT JOIN (company_employees c1_ INNER JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id) ON (c2_.id = c0_.salesPerson_id) WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" ); } @@ -2119,7 +2097,7 @@ public function testSingleTableInheritanceJoinCreatesOnCondition(): void // via a join association $this->assertSqlGeneration( 'SELECT c FROM Doctrine\Tests\Models\Company\CompanyContract c JOIN c.salesPerson s WHERE c.completed = true', - "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" + "SELECT c0_.id AS id_0, c0_.completed AS completed_1, c0_.fixPrice AS fixPrice_2, c0_.hoursWorked AS hoursWorked_3, c0_.pricePerHour AS pricePerHour_4, c0_.maxPrice AS maxPrice_5, c0_.discr AS discr_6, c0_.salesPerson_id AS salesPerson_id_7 FROM company_contracts c0_ INNER JOIN company_employees c1_ ON c0_.salesPerson_id = c1_.id LEFT JOIN company_persons c2_ ON c1_.id = c2_.id LEFT JOIN company_managers c3_ ON c1_.id = c3_.id WHERE (c0_.completed = 1) AND c0_.discr IN ('fix', 'flexible', 'flexultra')" ); } @@ -2131,7 +2109,7 @@ public function testSingleTableInheritanceCreatesOnConditionAndWhere(): void // into the ON clause of the join $this->assertSqlGeneration( 'SELECT e, COUNT(c) FROM Doctrine\Tests\Models\Company\CompanyEmployee e JOIN e.contracts c WHERE e.department = :department', - "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, COUNT(c2_.id) AS sclr_5, c0_.discr AS discr_6 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id INNER JOIN company_contract_employees c3_ ON c1_.id = c3_.employee_id INNER JOIN company_contracts c2_ ON c2_.id = c3_.contract_id AND c2_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?", + "SELECT c0_.id AS id_0, c0_.name AS name_1, c1_.salary AS salary_2, c1_.department AS department_3, c1_.startDate AS startDate_4, c2_.title AS title_5, COUNT(c3_.id) AS sclr_6, c0_.discr AS discr_7, c0_.spouse_id AS spouse_id_8, c2_.car_id AS car_id_9 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id LEFT JOIN company_managers c2_ ON c1_.id = c2_.id INNER JOIN company_contract_employees c4_ ON c1_.id = c4_.employee_id INNER JOIN company_contracts c3_ ON c3_.id = c4_.contract_id AND c3_.discr IN ('fix', 'flexible', 'flexultra') WHERE c1_.department = ?", [], ['department' => 'foobar'] ); @@ -2160,7 +2138,7 @@ public function testHavingSupportResultVariableNullComparisonExpression(): void { $this->assertSqlGeneration( 'SELECT u AS user, SUM(a.id) AS score FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN Doctrine\Tests\Models\CMS\CmsAddress a WITH a.user = u GROUP BY u HAVING score IS NOT NULL AND score >= 5', - 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, SUM(c1_.id) AS sclr_4 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON (c1_.user_id = c0_.id) GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id HAVING sclr_4 IS NOT NULL AND sclr_4 >= 5' + 'SELECT c0_.id AS id_0, c0_.status AS status_1, c0_.username AS username_2, c0_.name AS name_3, SUM(c1_.id) AS sclr_4, c0_.email_id AS email_id_5 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON (c1_.user_id = c0_.id) GROUP BY c0_.id, c0_.status, c0_.username, c0_.name, c0_.email_id HAVING sclr_4 IS NOT NULL AND sclr_4 >= 5' ); } From 98b404875fd83e45e75441be30caa3920d4e2cbb Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Mon, 5 Jun 2023 17:22:46 +0000 Subject: [PATCH 11/19] Defer removing removed entities from to-many collections until after transaction commit --- lib/Doctrine/ORM/UnitOfWork.php | 120 +++++++------ .../Tests/ORM/Functional/GH10752Test.php | 157 ++++++++++++++++++ .../ManyToManyBasicAssociationTest.php | 9 + .../ORM/Functional/ManyToManyEventTest.php | 14 +- 4 files changed, 239 insertions(+), 61 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/GH10752Test.php diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index bc8af25fbf..5982947b24 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -24,7 +24,6 @@ use Doctrine\ORM\Event\PreRemoveEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Exception\ORMException; -use Doctrine\ORM\Exception\UnexpectedAssociationValue; use Doctrine\ORM\Id\AssignedGenerator; use Doctrine\ORM\Internal\CommitOrderCalculator; use Doctrine\ORM\Internal\HydrationCompleteHandler; @@ -240,6 +239,17 @@ class UnitOfWork implements PropertyChangedListener */ private $visitedCollections = []; + /** + * List of collections visited during the changeset calculation that contain to-be-removed + * entities and need to have keys removed post commit. + * + * Indexed by Collection object ID, which also serves as the key in self::$visitedCollections; + * values are the key names that need to be removed. + * + * @psalm-var array> + */ + private $pendingCollectionElementRemovals = []; + /** * The EntityManager that "owns" this UnitOfWork instance. * @@ -474,8 +484,15 @@ public function commit($entity = null) $this->afterTransactionComplete(); - // Take new snapshots from visited collections - foreach ($this->visitedCollections as $coll) { + // Unset removed entities from collections, and take new snapshots from + // all visited collections. + foreach ($this->visitedCollections as $coid => $coll) { + if (isset($this->pendingCollectionElementRemovals[$coid])) { + foreach ($this->pendingCollectionElementRemovals[$coid] as $key => $valueIgnored) { + unset($coll[$key]); + } + } + $coll->takeSnapshot(); } @@ -487,15 +504,16 @@ public function commit($entity = null) /** @param object|object[]|null $entity */ private function postCommitCleanup($entity): void { - $this->entityInsertions = - $this->entityUpdates = - $this->entityDeletions = - $this->extraUpdates = - $this->collectionUpdates = - $this->nonCascadedNewDetectedEntities = - $this->collectionDeletions = - $this->visitedCollections = - $this->orphanRemovals = []; + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->extraUpdates = + $this->collectionUpdates = + $this->nonCascadedNewDetectedEntities = + $this->collectionDeletions = + $this->pendingCollectionElementRemovals = + $this->visitedCollections = + $this->orphanRemovals = []; if ($entity === null) { $this->entityChangeSets = $this->scheduledForSynchronization = []; @@ -916,6 +934,14 @@ private function computeAssociationChanges(array $assoc, $value): void return; } + // If this collection is dirty, schedule it for updates + if ($value instanceof PersistentCollection && $value->isDirty()) { + $coid = spl_object_id($value); + + $this->collectionUpdates[$coid] = $value; + $this->visitedCollections[$coid] = $value; + } + // Look through the entities, and in any of their associations, // for transient (new) entities, recursively. ("Persistence by reachability") // Unwrap. Uninitialized collections will simply be empty. @@ -929,15 +955,6 @@ private function computeAssociationChanges(array $assoc, $value): void $state = $this->getEntityState($entry, self::STATE_NEW); - if (! ($entry instanceof $assoc['targetEntity'])) { - throw UnexpectedAssociationValue::create( - $assoc['sourceEntity'], - $assoc['fieldName'], - get_debug_type($entry), - $assoc['targetEntity'] - ); - } - switch ($state) { case self::STATE_NEW: if (! $assoc['isCascadePersist']) { @@ -960,29 +977,21 @@ private function computeAssociationChanges(array $assoc, $value): void case self::STATE_REMOVED: // Consume the $value as array (it's either an array or an ArrayAccess) // and remove the element from Collection. - if ($assoc['type'] & ClassMetadata::TO_MANY) { - unset($value[$key]); + if (! ($assoc['type'] & ClassMetadata::TO_MANY)) { + break; } - break; + $coid = spl_object_id($value); + $this->visitedCollections[$coid] = $value; - case self::STATE_DETACHED: - // Can actually not happen right now as we assume STATE_NEW, - // so the exception will be raised from the DBAL layer (constraint violation). - throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); + if (! isset($this->pendingCollectionElementRemovals[$coid])) { + $this->pendingCollectionElementRemovals[$coid] = []; + } - default: - // MANAGED associated entities are already taken into account - // during changeset calculation anyway, since they are in the identity map. + $this->pendingCollectionElementRemovals[$coid][$key] = true; + break; } } - - if ($value instanceof PersistentCollection && $value->isDirty()) { - $coid = spl_object_id($value); - - $this->collectionUpdates[$coid] = $value; - $this->visitedCollections[$coid] = $value; - } } /** @@ -2627,23 +2636,24 @@ public function getCommitOrderCalculator() public function clear($entityName = null) { if ($entityName === null) { - $this->identityMap = - $this->entityIdentifiers = - $this->originalEntityData = - $this->entityChangeSets = - $this->entityStates = - $this->scheduledForSynchronization = - $this->entityInsertions = - $this->entityUpdates = - $this->entityDeletions = - $this->nonCascadedNewDetectedEntities = - $this->collectionDeletions = - $this->collectionUpdates = - $this->extraUpdates = - $this->readOnlyObjects = - $this->visitedCollections = - $this->eagerLoadingEntities = - $this->orphanRemovals = []; + $this->identityMap = + $this->entityIdentifiers = + $this->originalEntityData = + $this->entityChangeSets = + $this->entityStates = + $this->scheduledForSynchronization = + $this->entityInsertions = + $this->entityUpdates = + $this->entityDeletions = + $this->nonCascadedNewDetectedEntities = + $this->collectionDeletions = + $this->collectionUpdates = + $this->extraUpdates = + $this->readOnlyObjects = + $this->pendingCollectionElementRemovals = + $this->visitedCollections = + $this->eagerLoadingEntities = + $this->orphanRemovals = []; } else { Deprecation::triggerIfCalledFromOutside( 'doctrine/orm', diff --git a/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php new file mode 100644 index 0000000000..58d9ba2eeb --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php @@ -0,0 +1,157 @@ +setUpEntitySchema([ + GH10752Order::class, + GH10752Promotion::class, + ]); + } + + public function testThrowExceptionWhenRemovingPromotionThatIsInUse(): void + { + if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { + self::markTestSkipped('Platform does not support foreign keys.'); + } + + $order = $this->createOrder(); + $promotion = $this->createPromotion(); + + $order->addPromotion($promotion); + + $this->_em->persist($order); + $this->_em->persist($promotion); + $this->_em->flush(); + + $this->_em->remove($promotion); + + $this->expectException(ForeignKeyConstraintViolationException::class); + $this->_em->flush(); + } + + public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotInMemory(): void + { + if (! $this->_em->getConnection()->getDatabasePlatform()->supportsForeignKeyConstraints()) { + self::markTestSkipped('Platform does not support foreign keys.'); + } + + $order = $this->createOrder(); + $promotion = $this->createPromotion(); + + $order->addPromotion($promotion); + + $this->_em->persist($order); + $this->_em->persist($promotion); + $this->_em->flush(); + + $this->_em->clear(); + + $promotion = $this->_em->find(GH10752Promotion::class, $promotion->getId()); + $this->_em->remove($promotion); + + $this->expectException(ForeignKeyConstraintViolationException::class); + $this->_em->flush(); + } + + private function createOrder(): GH10752Order + { + return new GH10752Order(); + } + + private function createPromotion(): GH10752Promotion + { + return new GH10752Promotion(); + } +} + +/** + * @ORM\Entity + */ +class GH10752Order +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var int + */ + private $id = null; + + /** + * @ORM\ManyToMany(targetEntity="GH10752Promotion", cascade={"persist"}) + * @ORM\JoinTable(name="order_promotion", + * joinColumns={@ORM\JoinColumn(name="order_id", referencedColumnName="id", onDelete="CASCADE")}, + * inverseJoinColumns={@ORM\JoinColumn(name="promotion_id", referencedColumnName="id")} + * ) + * + * @var Collection + */ + private $promotions; + + public function __construct() + { + $this->promotions = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getPromotions(): Collection + { + return $this->promotions; + } + + public function addPromotion(GH10752Promotion $promotion): void + { + if (! $this->promotions->contains($promotion)) { + $this->promotions->add($promotion); + } + } + + public function removePromotion(GH10752Promotion $promotion): void + { + if ($this->promotions->contains($promotion)) { + $this->promotions->removeElement($promotion); + } + } +} + +/** + * @ORM\Entity + */ +class GH10752Promotion +{ + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + * + * @var int + */ + private $id = null; + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index 71d63b7515..20fd7163f8 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -101,6 +101,9 @@ public function testManyToManyAddRemove(): void //$user->getGroups()->remove(0); $this->_em->flush(); + + self::assertFalse($user->getGroups()->isDirty()); + $this->_em->clear(); // Reload same user @@ -232,8 +235,14 @@ public function testRemoveGroupWithUser(): void } $this->_em->flush(); + + // Changes to in-memory collection have been made and flushed + self::assertCount(0, $user->getGroups()); + self::assertFalse($user->getGroups()->isDirty()); + $this->_em->clear(); + // Changes have been made to the database $newUser = $this->_em->find(get_class($user), $user->getId()); self::assertCount(0, $newUser->getGroups()); } diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php index 03d547e6b7..d9436acb23 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyEventTest.php @@ -30,21 +30,22 @@ protected function setUp(): void public function testListenerShouldBeNotifiedWhenNewCollectionEntryAdded(): void { - $user = $this->createNewValidUser(); + $user = $this->createNewValidUser(); + $group = new CmsGroup(); + $group->name = 'admins'; + $this->_em->persist($user); + $this->_em->persist($group); $this->_em->flush(); self::assertFalse($this->listener->wasNotified); - $group = new CmsGroup(); - $group->name = 'admins'; $user->addGroup($group); - $this->_em->persist($user); $this->_em->flush(); self::assertTrue($this->listener->wasNotified); } - public function testListenerShouldBeNotifiedWhenNewCollectionEntryRemoved(): void + public function testListenerShouldBeNotifiedWhenCollectionEntryRemoved(): void { $user = $this->createNewValidUser(); $group = new CmsGroup(); @@ -52,10 +53,11 @@ public function testListenerShouldBeNotifiedWhenNewCollectionEntryRemoved(): voi $user->addGroup($group); $this->_em->persist($user); + $this->_em->persist($group); $this->_em->flush(); self::assertFalse($this->listener->wasNotified); - $this->_em->remove($group); + $user->getGroups()->removeElement($group); $this->_em->flush(); self::assertTrue($this->listener->wasNotified); From 930fa831b2619e87580158ecd7232afc1708fd92 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Mon, 5 Jun 2023 21:32:30 +0000 Subject: [PATCH 12/19] Test expected query counts in ManyToManyBasicAssociationTest When collection updates/join table cleanups do not happen through specialized Entity-/CollectionPersister methods but instead as "plain" updates, we may issue a lot more queries than expected. --- .../ManyToManyBasicAssociationTest.php | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index 20fd7163f8..1284f89ef2 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -144,8 +144,13 @@ public function testManyToManyCollectionClearing(): void $user->groups->clear(); + $this->getQueryLog()->reset()->enable(); $this->_em->flush(); + // Deletions of entire collections happen in a single query + $this->removeTransactionCommandsFromQueryLog(); + self::assertQueryCount(1); + // Check that the links in the association table have been deleted $this->assertGblancoGroupCountIs(0); } @@ -215,12 +220,20 @@ public function testRetrieveManyToManyAndAddMore(): void /** @group DDC-130 */ public function testRemoveUserWithManyGroups(): void { - $user = $this->addCmsUserGblancoWithGroups(2); + $user = $this->addCmsUserGblancoWithGroups(10); $userId = $user->getId(); $this->_em->remove($user); + + $this->getQueryLog()->reset()->enable(); + $this->_em->flush(); + // This takes three queries: One to delete all user -> group join table rows for the user, + // one to delete all user -> tags join table rows for the user, and a final one to delete the user itself. + $this->removeTransactionCommandsFromQueryLog(); + self::assertQueryCount(3); + $newUser = $this->_em->find(get_class($user), $userId); self::assertNull($newUser); } @@ -228,14 +241,33 @@ public function testRemoveUserWithManyGroups(): void /** @group DDC-130 */ public function testRemoveGroupWithUser(): void { - $user = $this->addCmsUserGblancoWithGroups(2); + $user = $this->addCmsUserGblancoWithGroups(5); + + $anotherUser = new CmsUser(); + $anotherUser->username = 'joe_doe'; + $anotherUser->name = 'Joe Doe'; + $anotherUser->status = 'QA Engineer'; + + foreach ($user->getGroups() as $group) { + $anotherUser->addGroup($group); + } + + $this->_em->persist($anotherUser); + $this->_em->flush(); foreach ($user->getGroups() as $group) { $this->_em->remove($group); } + $this->getQueryLog()->reset()->enable(); $this->_em->flush(); + // This takes 5 * 2 queries – for each group to be removed, one to remove all join table rows + // for the CmsGroup -> CmsUser inverse side association (for both users at once), + // and one for the group itself. + $this->removeTransactionCommandsFromQueryLog(); + self::assertQueryCount(10); + // Changes to in-memory collection have been made and flushed self::assertCount(0, $user->getGroups()); self::assertFalse($user->getGroups()->isDirty()); @@ -252,7 +284,13 @@ public function testDereferenceCollectionDelete(): void $user = $this->addCmsUserGblancoWithGroups(2); $user->groups = null; + $this->getQueryLog()->reset()->enable(); $this->_em->flush(); + + // It takes one query to remove all join table rows for the user at once + $this->removeTransactionCommandsFromQueryLog(); + self::assertQueryCount(1); + $this->_em->clear(); $newUser = $this->_em->find(get_class($user), $user->getId()); @@ -537,4 +575,15 @@ public function testMatching(): void self::assertFalse($user->groups->isInitialized(), 'Post-condition: matching does not initialize collection'); } + + private function removeTransactionCommandsFromQueryLog(): void + { + $log = $this->getQueryLog(); + + foreach ($log->queries as $key => $entry) { + if ($entry['sql'] === '"START TRANSACTION"' || $entry['sql'] === '"COMMIT"') { + unset($log->queries[$key]); + } + } + } } From ee0b3f214ddfdbd796967cd9ba15acd7ee85dde9 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Wed, 21 Jun 2023 19:12:48 +0200 Subject: [PATCH 13/19] Write tests more concisely --- .../Tests/ORM/Functional/GH10752Test.php | 46 +++---------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php index 58d9ba2eeb..c90d199500 100644 --- a/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/GH10752Test.php @@ -11,7 +11,7 @@ use Doctrine\Tests\OrmFunctionalTestCase; /** - * @Group GH10752 + * @group GH10752 */ class GH10752Test extends OrmFunctionalTestCase { @@ -31,8 +31,8 @@ public function testThrowExceptionWhenRemovingPromotionThatIsInUse(): void self::markTestSkipped('Platform does not support foreign keys.'); } - $order = $this->createOrder(); - $promotion = $this->createPromotion(); + $order = new GH10752Order(); + $promotion = new GH10752Promotion(); $order->addPromotion($promotion); @@ -52,8 +52,8 @@ public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotI self::markTestSkipped('Platform does not support foreign keys.'); } - $order = $this->createOrder(); - $promotion = $this->createPromotion(); + $order = new GH10752Order(); + $promotion = new GH10752Promotion(); $order->addPromotion($promotion); @@ -63,22 +63,12 @@ public function testThrowExceptionWhenRemovingPromotionThatIsInUseAndOrderIsNotI $this->_em->clear(); - $promotion = $this->_em->find(GH10752Promotion::class, $promotion->getId()); + $promotion = $this->_em->find(GH10752Promotion::class, $promotion->id); $this->_em->remove($promotion); $this->expectException(ForeignKeyConstraintViolationException::class); $this->_em->flush(); } - - private function createOrder(): GH10752Order - { - return new GH10752Order(); - } - - private function createPromotion(): GH10752Promotion - { - return new GH10752Promotion(); - } } /** @@ -111,29 +101,12 @@ public function __construct() $this->promotions = new ArrayCollection(); } - public function getId(): ?int - { - return $this->id; - } - - public function getPromotions(): Collection - { - return $this->promotions; - } - public function addPromotion(GH10752Promotion $promotion): void { if (! $this->promotions->contains($promotion)) { $this->promotions->add($promotion); } } - - public function removePromotion(GH10752Promotion $promotion): void - { - if ($this->promotions->contains($promotion)) { - $this->promotions->removeElement($promotion); - } - } } /** @@ -148,10 +121,5 @@ class GH10752Promotion * * @var int */ - private $id = null; - - public function getId(): ?int - { - return $this->id; - } + public $id = null; } From ba089e551a1772228531fc2fbee1b5b61900fda4 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Mon, 5 Jun 2023 22:17:06 +0000 Subject: [PATCH 14/19] Avoid unnecessary changes --- lib/Doctrine/ORM/UnitOfWork.php | 19 +++++++++++++++++++ .../ManyToManyBasicAssociationTest.php | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 5982947b24..ebb5f12a01 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -24,6 +24,7 @@ use Doctrine\ORM\Event\PreRemoveEventArgs; use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Exception\ORMException; +use Doctrine\ORM\Exception\UnexpectedAssociationValue; use Doctrine\ORM\Id\AssignedGenerator; use Doctrine\ORM\Internal\CommitOrderCalculator; use Doctrine\ORM\Internal\HydrationCompleteHandler; @@ -955,6 +956,15 @@ private function computeAssociationChanges(array $assoc, $value): void $state = $this->getEntityState($entry, self::STATE_NEW); + if (! ($entry instanceof $assoc['targetEntity'])) { + throw UnexpectedAssociationValue::create( + $assoc['sourceEntity'], + $assoc['fieldName'], + get_debug_type($entry), + $assoc['targetEntity'] + ); + } + switch ($state) { case self::STATE_NEW: if (! $assoc['isCascadePersist']) { @@ -990,6 +1000,15 @@ private function computeAssociationChanges(array $assoc, $value): void $this->pendingCollectionElementRemovals[$coid][$key] = true; break; + + case self::STATE_DETACHED: + // Can actually not happen right now as we assume STATE_NEW, + // so the exception will be raised from the DBAL layer (constraint violation). + throw ORMInvalidArgumentException::detachedEntityFoundThroughRelationship($assoc, $entry); + + default: + // MANAGED associated entities are already taken into account + // during changeset calculation anyway, since they are in the identity map. } } } diff --git a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php index 1284f89ef2..994d2bb8aa 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ManyToManyBasicAssociationTest.php @@ -243,10 +243,10 @@ public function testRemoveGroupWithUser(): void { $user = $this->addCmsUserGblancoWithGroups(5); - $anotherUser = new CmsUser(); + $anotherUser = new CmsUser(); $anotherUser->username = 'joe_doe'; - $anotherUser->name = 'Joe Doe'; - $anotherUser->status = 'QA Engineer'; + $anotherUser->name = 'Joe Doe'; + $anotherUser->status = 'QA Engineer'; foreach ($user->getGroups() as $group) { $anotherUser->addGroup($group); From c2359015447ce052e036e3b0cf2213c2b3ec9176 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 6 Jun 2023 07:48:33 +0000 Subject: [PATCH 15/19] Move a check up (continue early) --- lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php index 623c2cb61c..1f6d3e5127 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -538,7 +538,7 @@ final protected function updateTable( protected function deleteJoinTableRecords(array $identifier, array $types): void { foreach ($this->class->associationMappings as $mapping) { - if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY) { + if ($mapping['type'] !== ClassMetadata::MANY_TO_MANY || isset($mapping['isOnDeleteCascade'])) { continue; } @@ -574,10 +574,6 @@ protected function deleteJoinTableRecords(array $identifier, array $types): void $otherKeys[] = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform); } - if (isset($mapping['isOnDeleteCascade'])) { - continue; - } - $joinTableName = $this->quoteStrategy->getJoinTableName($association, $this->class, $this->platform); $this->conn->delete($joinTableName, array_combine($keys, $identifier), $types); From d220494edc583f00baeeab3a1456019c39aae701 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 6 Jun 2023 21:29:55 +0000 Subject: [PATCH 16/19] Improve documentation on exact behaviour of many-to-many deletion operations --- docs/en/reference/association-mapping.rst | 18 ++++++++ docs/en/reference/working-with-objects.rst | 50 +++++++++++++++++++--- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index 1ff30d3df6..6280eee061 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -881,6 +881,15 @@ Generated MySQL Schema: replaced by one-to-many/many-to-one associations between the 3 participating classes. +.. note:: + + For many-to-many associations, the ORM takes care of managing rows + in the join table connecting both sides. Due to the way it deals + with entity removals, database-level constraints may not work the + way one might intuitively assume. Thus, be sure not to miss the section + on :ref:`join table management `. + + Many-To-Many, Bidirectional --------------------------- @@ -1019,6 +1028,15 @@ one is bidirectional. The MySQL schema is exactly the same as for the Many-To-Many uni-directional case above. +.. note:: + + For many-to-many associations, the ORM takes care of managing rows + in the join table connecting both sides. Due to the way it deals + with entity removals, database-level constraints may not work the + way one might intuitively assume. Thus, be sure not to miss the section + on :ref:`join table management `. + + Owning and Inverse Side on a ManyToMany Association ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/en/reference/working-with-objects.rst b/docs/en/reference/working-with-objects.rst index fb1cc1ae43..32d53212e6 100644 --- a/docs/en/reference/working-with-objects.rst +++ b/docs/en/reference/working-with-objects.rst @@ -286,17 +286,53 @@ as follows: After an entity has been removed, its in-memory state is the same as before the removal, except for generated identifiers. -Removing an entity will also automatically delete any existing -records in many-to-many join tables that link this entity. The -action taken depends on the value of the ``@joinColumn`` mapping -attribute "onDelete". Either Doctrine issues a dedicated ``DELETE`` -statement for records of each join table or it depends on the -foreign key semantics of onDelete="CASCADE". +During the ``EntityManager#flush()`` operation, the removed entity +will also be removed from all collections in entities currently +loaded into memory. + +.. _remove_object_many_to_many_join_tables: + +Join-table management when removing from many-to-many collections +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Regarding existing rows in many-to-many join tables that refer to +an entity being removed, the following applies. + +When the entity being removed does not declare the many-to-many association +itself (that is, the many-to-many association is unidirectional and +the entity is on the inverse side), the ORM has no reasonable way to +detect associations targeting the entity's class. Thus, no ORM-level handling +of join-table rows is attempted and database-level constraints apply. +In case of database-level ``ON DELETE RESTRICT`` constraints, the +``EntityManager#flush()`` operation may abort and a ``ConstraintViolationException`` +may be thrown. No in-memory collections will be modified in this case. +With ``ON DELETE CASCADE``, the RDBMS will take care of removing rows +from join tables. + +When the entity being removed is part of bi-directional many-to-many +association, either as the owning or inverse side, the ORM will +delete rows from join tables before removing the entity itself. That means +database-level ``ON DELETE RESTRICT`` constraints on join tables are not +effective, since the join table rows are removed first. Removal of join table +rows happens through specialized methods in entity and collection persister +classes and take one query per entity and join table. In case the association +uses a ``@JoinColumn`` configuration with ``onDelete="CASCADE"``, instead +of using a dedicated ``DELETE`` query the database-level operation will be +relied upon. + +.. note:: + + In case you rely on database-level ``ON DELETE RESTRICT`` constraints, + be aware that by making many-to-many associations bidirectional the + assumed protection may be lost. + + +Performance of different deletion strategies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Deleting an object with all its associated objects can be achieved in multiple ways with very different performance impacts. - 1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM will fetch this association. If its a Single association it will pass this entity to From 4c3bd208018c26498e5f682aaad45fa00ea307d5 Mon Sep 17 00:00:00 2001 From: Alexander Dmitryuk Date: Thu, 22 Jun 2023 19:36:06 +0700 Subject: [PATCH 17/19] Fix missing setFilterSchemaAssetsExpression in phpdoc (#10776) --- .../ORM/Tools/Console/Command/ConvertMappingCommand.php | 8 +++++++- .../Tools/Console/Command/SchemaTool/CreateCommand.php | 8 +++++++- .../ORM/Tools/Console/Command/SchemaTool/DropCommand.php | 8 +++++++- .../Tools/Console/Command/SchemaTool/UpdateCommand.php | 8 +++++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php index 224b3a391c..de4c3f4065 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/ConvertMappingCommand.php @@ -73,7 +73,13 @@ protected function configure() by the ORM, you can use a DBAL functionality to filter the tables and sequences down on a global level: - $config->setFilterSchemaAssetsExpression($regexp); + $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool { + if ($assetName instanceof AbstractAsset) { + $assetName = $assetName->getName(); + } + + return !str_starts_with($assetName, 'audit_'); + }); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php index fc294e78ef..2438d4109f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/CreateCommand.php @@ -33,7 +33,13 @@ protected function configure() by the ORM, you can use a DBAL functionality to filter the tables and sequences down on a global level: - $config->setFilterSchemaAssetsExpression($regexp); + $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool { + if ($assetName instanceof AbstractAsset) { + $assetName = $assetName->getName(); + } + + return !str_starts_with($assetName, 'audit_'); + }); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php index 5ba628f8d6..c312675392 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/DropCommand.php @@ -37,7 +37,13 @@ protected function configure() by the ORM, you can use a DBAL functionality to filter the tables and sequences down on a global level: - $config->setFilterSchemaAssetsExpression($regexp); + $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool { + if ($assetName instanceof AbstractAsset) { + $assetName = $assetName->getName(); + } + + return !str_starts_with($assetName, 'audit_'); + }); EOT ); } diff --git a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php index 184e63aeae..3d2631ec0f 100644 --- a/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php +++ b/lib/Doctrine/ORM/Tools/Console/Command/SchemaTool/UpdateCommand.php @@ -61,7 +61,13 @@ protected function configure() by the ORM, you can use a DBAL functionality to filter the tables and sequences down on a global level: - $config->setFilterSchemaAssetsExpression($regexp); + $config->setSchemaAssetsFilter(function (string|AbstractAsset $assetName): bool { + if ($assetName instanceof AbstractAsset) { + $assetName = $assetName->getName(); + } + + return !str_starts_with($assetName, 'audit_'); + }); EOT ); } From 5114dcee0b9940905a9f05b124191d385d3f2188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 23 Jun 2023 17:43:11 +0200 Subject: [PATCH 18/19] Work around slevomat/coding-standard issues I tweaked the code so that it would not fall victim to https://github.com/slevomat/coding-standard/issues/1585 or https://github.com/slevomat/coding-standard/issues/1586, thus fixing the phpcs job without losing information or breaking other jobs. --- lib/Doctrine/ORM/Configuration.php | 4 ++-- .../ORM/Internal/Hydration/ObjectHydrator.php | 1 - .../ORM/Query/Exec/AbstractSqlExecutor.php | 7 +++---- lib/Doctrine/ORM/Query/Parser.php | 3 ++- phpstan-baseline.neon | 15 --------------- .../Export/ClassMetadataExporterTestCase.php | 10 ++++++---- .../Tests/ORM/Tools/SchemaValidatorTest.php | 3 ++- 7 files changed, 15 insertions(+), 28 deletions(-) diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index aebd182d77..a8ad0d2712 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -208,10 +208,10 @@ public function newDefaultAnnotationDriver($paths = [], $useSimpleAnnotationRead } /** - * @deprecated No replacement planned. - * * Adds a namespace under a certain alias. * + * @deprecated No replacement planned. + * * @param string $alias * @param string $namespace * diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index c2fb4e99bc..74146dbf86 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -313,7 +313,6 @@ static function (string $fieldName) use ($data, $class) { * that belongs to a particular component/class. Afterwards, all these chunks * are processed, one after the other. For each chunk of class data only one of the * following code paths is executed: - * * Path A: The data chunk belongs to a joined/associated object and the association * is collection-valued. * Path B: The data chunk belongs to a joined/associated object and the association diff --git a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php index 737d611a34..fc9660d1bb 100644 --- a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -53,10 +53,9 @@ public function removeQueryCacheProfile() /** * Executes all sql statements. * - * @param Connection $conn The database connection that is used to execute the queries. - * @psalm-param list|array $params The parameters. - * @psalm-param array| - * array $types The parameter types. + * @param Connection $conn The database connection that is used to execute the queries. + * @param list|array $params The parameters. + * @param array|array $types The parameter types. * * @return Result|int */ diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 318c21e9a1..4cfe233a1e 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2566,7 +2566,8 @@ public function ConditionalPrimary() * EmptyCollectionComparisonExpression | CollectionMemberExpression | * InstanceOfExpression * - * @return AST\BetweenExpression| + * @return AST\Node + * @psalm-return AST\BetweenExpression| * AST\CollectionMemberExpression| * AST\ComparisonExpression| * AST\EmptyCollectionComparisonExpression| diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4ed685ea39..42b40438d7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -395,21 +395,6 @@ parameters: count: 1 path: lib/Doctrine/ORM/Query/Parser.php - - - message: """ - #^PHPDoc tag @return has invalid value \\(AST\\\\BetweenExpression\\| - AST\\\\CollectionMemberExpression\\| - AST\\\\ComparisonExpression\\| - AST\\\\EmptyCollectionComparisonExpression\\| - AST\\\\ExistsExpression\\| - AST\\\\InExpression\\| - AST\\\\InstanceOfExpression\\| - AST\\\\LikeExpression\\| - AST\\\\NullComparisonExpression\\)\\: Unexpected token "\\\\n \\* ", expected type at offset 344$# - """ - count: 1 - path: lib/Doctrine/ORM/Query/Parser.php - - message: "#^Parameter \\#2 \\$stringPattern of class Doctrine\\\\ORM\\\\Query\\\\AST\\\\LikeExpression constructor expects Doctrine\\\\ORM\\\\Query\\\\AST\\\\Functions\\\\FunctionNode\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\InputParameter\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\Literal\\|Doctrine\\\\ORM\\\\Query\\\\AST\\\\PathExpression, Doctrine\\\\ORM\\\\Query\\\\AST\\\\Node given\\.$#" count: 1 diff --git a/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php b/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php index 8f1cad3c45..99bfa22b80 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php +++ b/tests/Doctrine/Tests/ORM/Tools/Export/ClassMetadataExporterTestCase.php @@ -15,6 +15,8 @@ use Doctrine\ORM\Mapping\Driver\AnnotationDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; use Doctrine\ORM\Mapping\Driver\YamlDriver; +use Doctrine\ORM\Mapping\PostPersist; +use Doctrine\ORM\Mapping\PrePersist; use Doctrine\ORM\Tools\DisconnectedClassMetadataFactory; use Doctrine\ORM\Tools\EntityGenerator; use Doctrine\ORM\Tools\Export\ClassMetadataExporter; @@ -399,26 +401,26 @@ class Group } class UserListener { - /** @\Doctrine\ORM\Mapping\PrePersist */ + /** @PrePersist */ public function customPrePersist(): void { } - /** @\Doctrine\ORM\Mapping\PostPersist */ + /** @PostPersist */ public function customPostPersist(): void { } } class GroupListener { - /** @\Doctrine\ORM\Mapping\PrePersist */ + /** @PrePersist */ public function prePersist(): void { } } class AddressListener { - /** @\Doctrine\ORM\Mapping\PostPersist */ + /** @PostPersist */ public function customPostPersist(): void { } diff --git a/tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php b/tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php index 3b1a9245bd..b4195d0e2e 100644 --- a/tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/SchemaValidatorTest.php @@ -384,7 +384,8 @@ class DDC1649Two { /** * @var DDC1649One - * @Id @ManyToOne(targetEntity="DDC1649One") + * @Id + * @ManyToOne(targetEntity="DDC1649One") */ public $one; } From 0e06d6b67d42bdbd39551f7fb97898d67cee30bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Fri, 23 Jun 2023 17:50:02 +0200 Subject: [PATCH 19/19] Apply latest coding standard rules --- tests/Doctrine/Tests/ORM/Functional/Ticket/GH5804Test.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH5804Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH5804Test.php index f66cb82bfe..5790a54ad1 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH5804Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH5804Test.php @@ -103,7 +103,7 @@ class GH5804Article * @Id * @Column(type="GH5804Type", length=255) * @GeneratedValue(strategy="CUSTOM") - * @CustomIdGenerator(class=\Doctrine\Tests\ORM\Functional\Ticket\GH5804Generator::class) + * @CustomIdGenerator(class=GH5804Generator::class) */ public $id;