From fca1ef78a7a05b67ad606cc8a7a70db6bcde5f56 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Wed, 12 Apr 2023 17:31:38 +0200 Subject: [PATCH 1/5] Handle null comparisons in `ManyToManyPersister` (#10587) * Add test case for https://github.com/doctrine/orm/issues/7717 * Do not hide null equality checks in `SqlValueVisitor::walkComparison` * Annotate `GH7717Parent::$children` type --- .../Collection/ManyToManyPersister.php | 14 ++++-- .../Entity/BasicEntityPersister.php | 16 ++++--- .../ORM/Persisters/SqlValueVisitor.php | 12 +---- .../Tests/Models/GH7717/GH7717Child.php | 26 +++++++++++ .../Tests/Models/GH7717/GH7717Parent.php | 29 ++++++++++++ .../ORM/Functional/Ticket/GH7717Test.php | 45 +++++++++++++++++++ 6 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/GH7717/GH7717Child.php create mode 100644 tests/Doctrine/Tests/Models/GH7717/GH7717Parent.php create mode 100755 tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php diff --git a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php index 0508aa2104..892cdd3a7f 100644 --- a/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/Collection/ManyToManyPersister.php @@ -6,6 +6,7 @@ use BadMethodCallException; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Expr\Comparison; use Doctrine\DBAL\Exception as DBALException; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\PersistentCollection; @@ -246,10 +247,15 @@ public function loadCriteria(PersistentCollection $collection, Criteria $criteri foreach ($parameters as $parameter) { [$name, $value, $operator] = $parameter; - $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform); - $whereClauses[] = sprintf('te.%s %s ?', $field, $operator); - $params[] = $value; - $paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0]; + $field = $this->quoteStrategy->getColumnName($name, $targetClass, $this->platform); + + if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) { + $whereClauses[] = sprintf('te.%s %s NULL', $field, $operator === Comparison::EQ ? 'IS' : 'IS NOT'); + } else { + $whereClauses[] = sprintf('te.%s %s ?', $field, $operator); + $params[] = $value; + $paramTypes[] = PersisterHelper::getTypeOfField($name, $targetClass, $this->em)[0]; + } } $tableName = $this->quoteStrategy->getTableName($targetClass, $this->platform); diff --git a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php index 0b655254df..6629eb3734 100644 --- a/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php @@ -890,15 +890,17 @@ public function expandCriteriaParameters(Criteria $criteria) $valueVisitor->dispatch($expression); - [$params, $types] = $valueVisitor->getParamsAndTypes(); - - foreach ($params as $param) { - $sqlParams = array_merge($sqlParams, $this->getValues($param)); - } + [, $types] = $valueVisitor->getParamsAndTypes(); foreach ($types as $type) { - [$field, $value] = $type; - $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); + [$field, $value, $operator] = $type; + + if ($value === null && ($operator === Comparison::EQ || $operator === Comparison::NEQ)) { + continue; + } + + $sqlParams = array_merge($sqlParams, $this->getValues($value)); + $sqlTypes = array_merge($sqlTypes, $this->getTypes($field, $value, $this->class)); } return [$sqlParams, $sqlTypes]; diff --git a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php index a61d0a25f8..999746faaa 100644 --- a/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php +++ b/lib/Doctrine/ORM/Persisters/SqlValueVisitor.php @@ -27,18 +27,10 @@ class SqlValueVisitor extends ExpressionVisitor */ public function walkComparison(Comparison $comparison) { - $value = $this->getValueFromComparison($comparison); - $field = $comparison->getField(); - $operator = $comparison->getOperator(); - - if (($operator === Comparison::EQ || $operator === Comparison::IS) && $value === null) { - return null; - } elseif ($operator === Comparison::NEQ && $value === null) { - return null; - } + $value = $this->getValueFromComparison($comparison); $this->values[] = $value; - $this->types[] = [$field, $value, $operator]; + $this->types[] = [$comparison->getField(), $value, $comparison->getOperator()]; return null; } diff --git a/tests/Doctrine/Tests/Models/GH7717/GH7717Child.php b/tests/Doctrine/Tests/Models/GH7717/GH7717Child.php new file mode 100644 index 0000000000..0ea30c8437 --- /dev/null +++ b/tests/Doctrine/Tests/Models/GH7717/GH7717Child.php @@ -0,0 +1,26 @@ + + */ + public Selectable $children; +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php new file mode 100755 index 0000000000..96c519e862 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7717Test.php @@ -0,0 +1,45 @@ +createSchemaForModels( + GH7717Parent::class, + GH7717Child::class + ); + } + + public function testManyToManyPersisterIsNullComparison(): void + { + $childWithNullProperty = new GH7717Child(); + $childWithoutNullProperty = new GH7717Child(); + $childWithoutNullProperty->nullableProperty = 'nope'; + + $parent = new GH7717Parent(); + $parent->children = new ArrayCollection([$childWithNullProperty, $childWithoutNullProperty]); + + $this->_em->persist($parent); + $this->_em->flush(); + $this->_em->clear(); + + $parent = $this->_em->find(GH7717Parent::class, 1); + + $this->assertCount(1, $parent->children->matching(new Criteria(Criteria::expr()->isNull('nullableProperty')))); + } +} From a16aeaeac83f023f1121eb650765e7d1f9eb953a Mon Sep 17 00:00:00 2001 From: Eric COURTIAL Date: Wed, 12 Apr 2023 21:02:36 +0200 Subject: [PATCH 2/5] fix typo in HydrationCompleteHandler doc --- lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php b/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php index 2ca0684723..393255c0c5 100644 --- a/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php +++ b/lib/Doctrine/ORM/Internal/HydrationCompleteHandler.php @@ -51,7 +51,7 @@ public function deferPostLoadInvoking(ClassMetadata $class, $entity): void } /** - * This method should me called after any hydration cycle completed. + * This method should be called after any hydration cycle completed. * * Method fires all deferred invocations of postLoad events */ From 5ac6fadf29f26129d0db2716dfa45eff724c914e Mon Sep 17 00:00:00 2001 From: Tomas Mihalicka Date: Fri, 14 Apr 2023 18:11:27 +0200 Subject: [PATCH 3/5] Fixed xsd schema for support FQCN type After update to orm 2.14.2 invalid xsd schema error is occured, when in field,id or attribute-override have type is FQCN --- doctrine-mapping.xsd | 13 +++++++--- .../Doctrine/Tests/Models/Project/Project.php | 24 ++++++++++++++++++ .../Tests/Models/Project/ProjectId.php | 18 +++++++++++++ .../Models/Project/ProjectInvalidMapping.php | 24 ++++++++++++++++++ .../Tests/Models/Project/ProjectName.php | 18 +++++++++++++ .../ORM/Mapping/XmlMappingDriverTest.php | 25 +++++++++++++++++++ ...trine.Tests.Models.Project.Project.dcm.xml | 12 +++++++++ ...dels.Project.ProjectInvalidMapping.dcm.xml | 12 +++++++++ 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 tests/Doctrine/Tests/Models/Project/Project.php create mode 100644 tests/Doctrine/Tests/Models/Project/ProjectId.php create mode 100644 tests/Doctrine/Tests/Models/Project/ProjectInvalidMapping.php create mode 100644 tests/Doctrine/Tests/Models/Project/ProjectName.php create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.Project.dcm.xml create mode 100644 tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.ProjectInvalidMapping.dcm.xml diff --git a/doctrine-mapping.xsd b/doctrine-mapping.xsd index ebdb12902b..162e45f774 100644 --- a/doctrine-mapping.xsd +++ b/doctrine-mapping.xsd @@ -302,7 +302,7 @@ - + @@ -414,7 +414,7 @@ - + @@ -446,6 +446,13 @@ + + + + + + + @@ -630,7 +637,7 @@ - + diff --git a/tests/Doctrine/Tests/Models/Project/Project.php b/tests/Doctrine/Tests/Models/Project/Project.php new file mode 100644 index 0000000000..fc64b43561 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Project/Project.php @@ -0,0 +1,24 @@ +id = $id; + $this->name = $name; + } +} diff --git a/tests/Doctrine/Tests/Models/Project/ProjectId.php b/tests/Doctrine/Tests/Models/Project/ProjectId.php new file mode 100644 index 0000000000..cedc0e5bd9 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Project/ProjectId.php @@ -0,0 +1,18 @@ +id = $id; + } +} diff --git a/tests/Doctrine/Tests/Models/Project/ProjectInvalidMapping.php b/tests/Doctrine/Tests/Models/Project/ProjectInvalidMapping.php new file mode 100644 index 0000000000..ceecd48f75 --- /dev/null +++ b/tests/Doctrine/Tests/Models/Project/ProjectInvalidMapping.php @@ -0,0 +1,24 @@ +id = $id; + $this->name = $name; + } +} diff --git a/tests/Doctrine/Tests/Models/Project/ProjectName.php b/tests/Doctrine/Tests/Models/Project/ProjectName.php new file mode 100644 index 0000000000..ea2177996d --- /dev/null +++ b/tests/Doctrine/Tests/Models/Project/ProjectName.php @@ -0,0 +1,18 @@ +name = $name; + } +} diff --git a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php index df09f9613d..090a9d1769 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/XmlMappingDriverTest.php @@ -22,6 +22,10 @@ use Doctrine\Tests\Models\Generic\BooleanModel; use Doctrine\Tests\Models\GH7141\GH7141Article; use Doctrine\Tests\Models\GH7316\GH7316Article; +use Doctrine\Tests\Models\Project\Project; +use Doctrine\Tests\Models\Project\ProjectId; +use Doctrine\Tests\Models\Project\ProjectInvalidMapping; +use Doctrine\Tests\Models\Project\ProjectName; use Doctrine\Tests\Models\ValueObjects\Name; use Doctrine\Tests\Models\ValueObjects\Person; @@ -239,6 +243,10 @@ public static function dataInvalidSchema(): array UserMissingAttributes::class, ['The attribute \'name\' is required but missing' => 1], ], + [ + ProjectInvalidMapping::class, + ['attribute \'type\': [facet \'pattern\'] The value' => 2], + ], ]; } @@ -279,6 +287,23 @@ public function testInvalidEntityOrMappedSuperClassShouldMentionParentClasses(): $this->createClassMetadata(DDC889Class::class); } + + public function testClassNameInFieldOrId(): void + { + $class = new ClassMetadata(Project::class); + $class->initializeReflection(new RuntimeReflectionService()); + + $driver = $this->loadDriver(); + $driver->loadMetadataForClass(Project::class, $class); + + /** @var array{type: string} $id */ + $id = $class->getFieldMapping('id'); + /** @var array{type: string} $name */ + $name = $class->getFieldMapping('name'); + + self::assertEquals(ProjectId::class, $id['type']); + self::assertEquals(ProjectName::class, $name['type']); + } } class CTI diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.Project.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.Project.dcm.xml new file mode 100644 index 0000000000..05ad513b7b --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.Project.dcm.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.ProjectInvalidMapping.dcm.xml b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.ProjectInvalidMapping.dcm.xml new file mode 100644 index 0000000000..d70800a355 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Mapping/xml/Doctrine.Tests.Models.Project.ProjectInvalidMapping.dcm.xml @@ -0,0 +1,12 @@ + + + + + + + + + From 2977933119624139da2ab927293af700b55507b3 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sat, 15 Apr 2023 10:54:31 +0200 Subject: [PATCH 4/5] Run tests on SQLite with foreign keys enabled (#10632) --- tests/Doctrine/Tests/TestUtil.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/Doctrine/Tests/TestUtil.php b/tests/Doctrine/Tests/TestUtil.php index 28b3185c5f..4eb0afe22c 100644 --- a/tests/Doctrine/Tests/TestUtil.php +++ b/tests/Doctrine/Tests/TestUtil.php @@ -6,6 +6,7 @@ use Doctrine\Common\EventSubscriber; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\AbstractSQLiteDriver\Middleware\EnableForeignKeys; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -16,10 +17,12 @@ use UnexpectedValueException; use function assert; +use function class_exists; use function explode; use function fwrite; use function get_debug_type; use function getenv; +use function in_array; use function method_exists; use function sprintf; use function str_starts_with; @@ -63,7 +66,13 @@ public static function getConnection(): DbalExtensions\Connection self::$initialized = true; } - $connection = DriverManager::getConnection(self::getTestConnectionParameters()); + $connectionParameters = self::getTestConnectionParameters(); + $configuration = new Configuration(); + if (in_array($connectionParameters['driver'], ['pdo_sqlite', 'sqlite3'], true) && class_exists(EnableForeignKeys::class)) { + $configuration->setMiddlewares([new EnableForeignKeys()]); + } + + $connection = DriverManager::getConnection($connectionParameters, $configuration); assert($connection instanceof DbalExtensions\Connection); self::addDbEventSubscribers($connection); @@ -212,6 +221,7 @@ private static function mapConnectionParameters(array $configuration, string $pr 'port', 'server', 'memory', + 'path', 'ssl_key', 'ssl_cert', 'ssl_ca', From 6ca319a6f4a075657ef536542590a7c4562169b9 Mon Sep 17 00:00:00 2001 From: Yoann B Date: Thu, 20 Apr 2023 10:59:39 +0200 Subject: [PATCH 5/5] fix array association on partial index --- docs/en/reference/attributes-reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/reference/attributes-reference.rst b/docs/en/reference/attributes-reference.rst index 1f2fa339b9..2054e31a5d 100644 --- a/docs/en/reference/attributes-reference.rst +++ b/docs/en/reference/attributes-reference.rst @@ -575,7 +575,7 @@ Example with partial indexes: #[Index(name: "search_idx", columns: ["category"], options: [ - "where": "((category IS NOT NULL))" + "where" => "((category IS NOT NULL))" ] )] class ECommerceProduct