From 2005edf7b6e9b66383f58869894ea0c0da60b7e8 Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Fri, 11 Apr 2025 10:35:15 +0200 Subject: [PATCH 1/7] Fix HydratorFactory about nullable date field --- lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index a182d3963..fbe907c52 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -185,10 +185,13 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla <<<'EOF' // Field(type: "date") - if (isset($data['%1$s'])) { + if (isset($data['%1$s']) || (! empty($this->class->fieldMappings['%2$s']['nullable']) && array_key_exists('%1$s', $data))) { $value = $data['%1$s']; %3$s - $this->class->reflFields['%2$s']->setValue($document, clone $return); + if (\is_object($return)) { + $return = clone $return; + } + $this->class->reflFields['%2$s']->setValue($document, $return); $hydratedData['%2$s'] = $return; } From e1c95c89166723a543692be67fae87f384dc750d Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Fri, 11 Apr 2025 15:44:41 +0200 Subject: [PATCH 2/7] Does not use empty function (thanks to GromNaN) --- lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index fbe907c52..69f34a238 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -185,7 +185,7 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla <<<'EOF' // Field(type: "date") - if (isset($data['%1$s']) || (! empty($this->class->fieldMappings['%2$s']['nullable']) && array_key_exists('%1$s', $data))) { + if (array_key_exists('%1$s', $data) && ($data['%1$s'] !== null || ($this->class->fieldMappings['%2$s']['nullable'] ?? false))) { $value = $data['%1$s']; %3$s if (\is_object($return)) { From a85306f774a11ca7bcebf4ee49b1d4f0fc386975 Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Fri, 11 Apr 2025 16:38:30 +0200 Subject: [PATCH 3/7] Fix regression and add test to avoid regression about #2753 --- .../ODM/MongoDB/Hydrator/HydratorFactory.php | 6 ++---- phpunit.xml.dist | 4 ++-- .../ODM/MongoDB/Tests/Functional/DateTest.php | 14 ++++++++++++++ tests/Documents/User.php | 9 +++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index 69f34a238..cf2e7aeb1 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -188,10 +188,8 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla if (array_key_exists('%1$s', $data) && ($data['%1$s'] !== null || ($this->class->fieldMappings['%2$s']['nullable'] ?? false))) { $value = $data['%1$s']; %3$s - if (\is_object($return)) { - $return = clone $return; - } - $this->class->reflFields['%2$s']->setValue($document, $return); + $field = $this->class->reflFields['%2$s']; + if (null !== $return) { $field->setValue($document, clone $return); } else { $field->setValue($document, null); } $hydratedData['%2$s'] = $return; } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3860390bd..4865a653d 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -27,8 +27,8 @@ - - + + diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php index 35aa83b83..c258566ae 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php @@ -84,6 +84,20 @@ public function testDateInstanceChangeWhenValueDifferenceIsSubSecond(): void self::assertNotEmpty($changeset); } + // Test Issue #2753 + public function testNullableDateInstanceValue(): void + { + $user = new User(); + $user->setCreatedAt(new DateTime('1985-09-01')); + $this->dm->persist($user); + $this->dm->flush(); + $this->dm->clear(); + + $user = $this->dm->getRepository($user::class)->findOneBy([]); + self::assertInstanceOf(DateTime::class, $user->getCreatedAt()); + self::assertNull($user->getDisabledAt()); + } + public function testDateInstanceValueChangeDoesCauseUpdateIfValueIsTheSame(): void { $user = new User(); diff --git a/tests/Documents/User.php b/tests/Documents/User.php index f7ea6f859..642d8334d 100644 --- a/tests/Documents/User.php +++ b/tests/Documents/User.php @@ -34,6 +34,10 @@ class User extends BaseDocument #[ODM\Field(type: 'date')] protected $createdAt; + /** @var UTCDateTime|DateTimeInterface|string */ + #[ODM\Field(type: 'date', nullable: true, name: 'disable-at')] + protected ?\DateTimeInterface $disabledAt; + /** @var Address|null */ #[ODM\EmbedOne(targetDocument: Address::class)] protected $address; @@ -211,6 +215,11 @@ public function getCreatedAt() return $this->createdAt; } + public function getDisabledAt(): ?\DateTimeInterface + { + return $this->disabledAt; + } + public function getAddress(): ?Address { return $this->address; From 3bffd5c3d5ddaefeb950ec2dad7f0bdd08fa4d91 Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Fri, 11 Apr 2025 16:41:53 +0200 Subject: [PATCH 4/7] Rollback wrong commit, (I'm tired...) --- phpunit.xml.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4865a653d..3860390bd 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -27,8 +27,8 @@ - - + + From 77ff0ee1d12fa0874a825449cfb21985832b7737 Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Mon, 28 Apr 2025 10:47:28 +0200 Subject: [PATCH 5/7] Fix issue in CS Fixer and phpstan --- tests/Documents/User.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Documents/User.php b/tests/Documents/User.php index 642d8334d..cd1f1efd5 100644 --- a/tests/Documents/User.php +++ b/tests/Documents/User.php @@ -34,9 +34,9 @@ class User extends BaseDocument #[ODM\Field(type: 'date')] protected $createdAt; - /** @var UTCDateTime|DateTimeInterface|string */ + /** @var ?DateTimeInterface */ #[ODM\Field(type: 'date', nullable: true, name: 'disable-at')] - protected ?\DateTimeInterface $disabledAt; + protected ?DateTimeInterface $disabledAt; /** @var Address|null */ #[ODM\EmbedOne(targetDocument: Address::class)] @@ -215,7 +215,7 @@ public function getCreatedAt() return $this->createdAt; } - public function getDisabledAt(): ?\DateTimeInterface + public function getDisabledAt(): ?DateTimeInterface { return $this->disabledAt; } From a0451aeae07dc344b08f06cde3de9f3824f22714 Mon Sep 17 00:00:00 2001 From: Richard Deloge Date: Mon, 28 Apr 2025 11:06:27 +0200 Subject: [PATCH 6/7] Remove useless comments --- tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php | 1 - tests/Documents/User.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php index c258566ae..2e1b140de 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php @@ -84,7 +84,6 @@ public function testDateInstanceChangeWhenValueDifferenceIsSubSecond(): void self::assertNotEmpty($changeset); } - // Test Issue #2753 public function testNullableDateInstanceValue(): void { $user = new User(); diff --git a/tests/Documents/User.php b/tests/Documents/User.php index cd1f1efd5..7e1031ec3 100644 --- a/tests/Documents/User.php +++ b/tests/Documents/User.php @@ -34,7 +34,6 @@ class User extends BaseDocument #[ODM\Field(type: 'date')] protected $createdAt; - /** @var ?DateTimeInterface */ #[ODM\Field(type: 'date', nullable: true, name: 'disable-at')] protected ?DateTimeInterface $disabledAt; From 2f95960f6603a6c478042b33a09cd73c3cb28d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Mon, 28 Apr 2025 13:15:55 +0200 Subject: [PATCH 7/7] Remove $field variable --- lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index cf2e7aeb1..e5ac3ce7f 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -188,8 +188,7 @@ private function generateHydratorClass(ClassMetadata $class, string $hydratorCla if (array_key_exists('%1$s', $data) && ($data['%1$s'] !== null || ($this->class->fieldMappings['%2$s']['nullable'] ?? false))) { $value = $data['%1$s']; %3$s - $field = $this->class->reflFields['%2$s']; - if (null !== $return) { $field->setValue($document, clone $return); } else { $field->setValue($document, null); } + $this->class->reflFields['%2$s']->setValue($document, $return === null ? null : clone $return); $hydratedData['%2$s'] = $return; }