From 6a86b9ded1a255cc20e95432725b4d4264351116 Mon Sep 17 00:00:00 2001 From: Alan Poulain Date: Fri, 29 May 2020 10:38:25 +0200 Subject: [PATCH] Fix prepareQueryOrNewObj with object values In convertToDatabaseValue, fallback to Type::convertPHPToDatabaseValue if the field doesn't have a mapping. --- .../MongoDB/Persisters/DocumentPersister.php | 23 +++++++---- .../Functional/DocumentPersisterTest.php | 39 +++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index dfc0ef9629..eade555927 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -1012,10 +1012,7 @@ public function prepareQueryOrNewObj(array $query, bool $isNewObj = false) : arr $preparedQueryElements = $this->prepareQueryElement((string) $key, $value, null, true, $isNewObj); foreach ($preparedQueryElements as [$preparedKey, $preparedValue]) { - $preparedValue = Type::convertPHPToDatabaseValue($preparedValue); - if ($this->class->hasField($key)) { - $preparedValue = $this->convertToDatabaseValue($key, $preparedValue); - } + $preparedValue = $this->convertToDatabaseValue((string) $key, $preparedValue); $preparedQuery[$preparedKey] = $preparedValue; } } @@ -1024,7 +1021,7 @@ public function prepareQueryOrNewObj(array $query, bool $isNewObj = false) : arr } /** - * Converts a single value to its database representation based on the mapping type + * Converts a single value to its database representation based on the mapping type if possible. * * @param mixed $value * @@ -1032,8 +1029,16 @@ public function prepareQueryOrNewObj(array $query, bool $isNewObj = false) : arr */ private function convertToDatabaseValue(string $fieldName, $value) { - $mapping = $this->class->fieldMappings[$fieldName]; - $typeName = $mapping['type']; + $mapping = []; + $typeName = ''; + $dbValue = null; + $hasMapping = $this->class->hasField($fieldName); + if ($hasMapping) { + $mapping = $this->class->fieldMappings[$fieldName]; + $typeName = $mapping['type']; + } else { + $dbValue = Type::convertPHPToDatabaseValue($value); + } if (is_array($value)) { foreach ($value as $k => $v) { @@ -1043,6 +1048,10 @@ private function convertToDatabaseValue(string $fieldName, $value) return $value; } + if (! $hasMapping) { + return $dbValue; + } + if (! empty($mapping['reference']) || ! empty($mapping['embedded'])) { return $value; } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php index 4ef076c35e..38081e4eb5 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php @@ -5,6 +5,7 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; use Closure; +use DateTime; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\LockException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; @@ -15,6 +16,7 @@ use Generator; use InvalidArgumentException; use MongoDB\BSON\ObjectId; +use MongoDB\BSON\UTCDateTime; use MongoDB\Collection; use ReflectionProperty; use function get_class; @@ -461,6 +463,43 @@ public function testPrepareQueryOrNewObjWithDBRefReferenceToTargetDocumentWithHa $this->assertEquals($expected, $documentPersister->prepareQueryOrNewObj($value)); } + /** + * @dataProvider queryProviderForComplexRefWithObjectValue + */ + public function testPrepareQueryOrNewObjWithComplexRefToTargetDocumentFieldWithObjectValue(array $expected, array $query) + { + $class = DocumentPersisterTestDocument::class; + $documentPersister = $this->uow->getDocumentPersister($class); + + $this->assertEquals( + $expected, + $documentPersister->prepareQueryOrNewObj($query) + ); + } + + public static function queryProviderForComplexRefWithObjectValue() : Generator + { + yield 'Direct comparison' => [ + 'expected' => ['complexRef.date' => new UTCDateTime('1590710400000')], + 'query' => ['complexRef.date' => new DateTime('2020-05-29')], + ]; + + yield 'Operator with single value' => [ + 'expected' => ['complexRef.date' => ['$ne' => new UTCDateTime('1590710400000')]], + 'query' => ['complexRef.date' => ['$ne' => new DateTime('2020-05-29')]], + ]; + + yield 'Operator with multiple values' => [ + 'expected' => ['complexRef.date' => ['$in' => [new UTCDateTime('1590710400000'), new UTCDateTime('1590796800000')]]], + 'query' => ['complexRef.date' => ['$in' => [new DateTime('2020-05-29'), new DateTime('2020-05-30')]]], + ]; + + yield 'Nested operator' => [ + 'expected' => ['complexRef.date' => ['$not' => ['$in' => [new UTCDateTime('1590710400000'), new UTCDateTime('1590796800000')]]]], + 'query' => ['complexRef.date' => ['$not' => ['$in' => [new DateTime('2020-05-29'), new DateTime('2020-05-30')]]]], + ]; + } + public function testPrepareQueryOrNewObjWithEmbeddedReferenceToTargetDocumentWithNormalIdType() { $class = DocumentPersisterTestHashIdDocument::class;