From 6fd291ec45e432f57b72ed6dea1bb18855217229 Mon Sep 17 00:00:00 2001 From: Maciej Malarz Date: Sat, 23 Nov 2019 14:53:31 +0100 Subject: [PATCH] Handle date_immutable in versioning --- lib/Doctrine/ODM/MongoDB/LockException.php | 2 +- .../ODM/MongoDB/Mapping/ClassMetadata.php | 2 +- .../MongoDB/Persisters/DocumentPersister.php | 19 ++-- .../ODM/MongoDB/Tests/Functional/LockTest.php | 90 +++++++++++++++++-- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/LockException.php b/lib/Doctrine/ODM/MongoDB/LockException.php index 4d25c895ff..e0eef4e21c 100644 --- a/lib/Doctrine/ODM/MongoDB/LockException.php +++ b/lib/Doctrine/ODM/MongoDB/LockException.php @@ -45,6 +45,6 @@ public static function invalidLockFieldType(string $type) : self public static function invalidVersionFieldType(string $type) : self { - return new self('Invalid version field type ' . $type . '. Version field must be int or date.'); + return new self('Invalid version field type ' . $type . '. Version field must be int, integer, date or date_immutable.'); } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index b4933eb1cf..a9f38dabb1 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -1653,7 +1653,7 @@ public function isIdGeneratorNone() : bool */ public function setVersionMapping(array &$mapping) : void { - if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') { + if (! in_array($mapping['type'], [Type::INT, Type::INTEGER, Type::DATE, Type::DATE_IMMUTABLE], true)) { throw LockException::invalidVersionFieldType($mapping['type']); } diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index f774d0a207..a3a8b7cf49 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -6,6 +6,7 @@ use BadMethodCallException; use DateTime; +use DateTimeImmutable; use Doctrine\Common\Persistence\Mapping\MappingException; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Hydrator\HydratorException; @@ -211,11 +212,11 @@ public function executeInserts(array $options = []) : void if ($this->class->isVersioned) { $versionMapping = $this->class->fieldMappings[$this->class->versionField]; $nextVersion = null; - if ($versionMapping['type'] === 'int') { + if ($versionMapping['type'] === Type::INT || $versionMapping['type'] === Type::INTEGER) { $nextVersion = max(1, (int) $this->class->reflFields[$this->class->versionField]->getValue($document)); $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersion); - } elseif ($versionMapping['type'] === 'date') { - $nextVersionDateTime = new DateTime(); + } elseif ($versionMapping['type'] === Type::DATE || $versionMapping['type'] === Type::DATE_IMMUTABLE) { + $nextVersionDateTime = $versionMapping['type'] === Type::DATE ? new DateTime() : new DateTimeImmutable(); $nextVersion = Type::convertPHPToDatabaseValue($nextVersionDateTime); $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersionDateTime); } @@ -286,11 +287,11 @@ private function executeUpsert(object $document, array $options) : void if ($this->class->isVersioned) { $versionMapping = $this->class->fieldMappings[$this->class->versionField]; $nextVersion = null; - if ($versionMapping['type'] === 'int') { + if ($versionMapping['type'] === Type::INT || $versionMapping === Type::INTEGER) { $nextVersion = max(1, (int) $this->class->reflFields[$this->class->versionField]->getValue($document)); $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersion); - } elseif ($versionMapping['type'] === 'date') { - $nextVersionDateTime = new DateTime(); + } elseif ($versionMapping['type'] === Type::DATE || $versionMapping['type'] === Type::DATE_IMMUTABLE) { + $nextVersionDateTime = $versionMapping['type'] === Type::DATE ? new DateTime() : new DateTimeImmutable(); $nextVersion = Type::convertPHPToDatabaseValue($nextVersionDateTime); $this->class->reflFields[$this->class->versionField]->setValue($document, $nextVersionDateTime); } @@ -371,12 +372,12 @@ public function update(object $document, array $options = []) : void if ($this->class->isVersioned) { $versionMapping = $this->class->fieldMappings[$this->class->versionField]; $currentVersion = $this->class->reflFields[$this->class->versionField]->getValue($document); - if ($versionMapping['type'] === 'int') { + if ($versionMapping['type'] === Type::INT || $versionMapping['type'] === Type::INTEGER) { $nextVersion = $currentVersion + 1; $update['$inc'][$versionMapping['name']] = 1; $query[$versionMapping['name']] = $currentVersion; - } elseif ($versionMapping['type'] === 'date') { - $nextVersion = new DateTime(); + } elseif ($versionMapping['type'] === Type::DATE || $versionMapping['type'] === Type::DATE_IMMUTABLE) { + $nextVersion = $versionMapping['type'] === Type::DATE ? new DateTime() : new DateTimeImmutable(); $update['$set'][$versionMapping['name']] = Type::convertPHPToDatabaseValue($nextVersion); $query[$versionMapping['name']] = Type::convertPHPToDatabaseValue($currentVersion); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php index b22635d7a7..3d756e1c43 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php @@ -79,14 +79,14 @@ public function testMultipleFlushesDoIncrementalUpdates() $this->dm->persist($test); $this->dm->flush(); - $this->assertIsInt($test->getVersion()); + $this->assertInternalType('int', $test->getVersion()); $this->assertEquals($i + 1, $test->getVersion()); } } - public function testLockTimestampSetsDefaultValue() + public function testLockDateSetsDefaultValue() { - $test = new LockTimestamp(); + $test = new LockDate(); $test->title = 'Testing'; $this->assertNull($test->version, 'Pre-Condition'); @@ -106,11 +106,33 @@ public function testLockTimestampSetsDefaultValue() return $test; } - public function testLockTimestampSetsDefaultValueOnUpsert() + public function testLockDateImmutableSetsDefaultValue() + { + $test = new LockDateImmutable(); + $test->title = 'Testing'; + + $this->assertNull($test->version, 'Pre-Condition'); + + $this->dm->persist($test); + $this->dm->flush(); + + $date1 = $test->version; + + $this->assertInstanceOf('DateTimeImmutable', $date1); + + $test->title = 'changed'; + $this->dm->flush(); + + $this->assertNotSame($date1, $test->version); + + return $test; + } + + public function testLockDateSetsDefaultValueOnUpsert() { $id = new ObjectId(); - $test = new LockTimestamp(); + $test = new LockDate(); $test->title = 'Testing'; $test->id = $id; @@ -132,9 +154,52 @@ public function testLockTimestampSetsDefaultValueOnUpsert() return $test; } - public function testLockTimestampThrowsException() + public function testLockDateImmutableSetsDefaultValueOnUpsert() + { + $id = new ObjectId(); + + $test = new LockDateImmutable(); + $test->title = 'Testing'; + $test->id = $id; + + $this->assertNull($test->version, 'Pre-Condition'); + + $this->dm->persist($test); + $this->dm->flush(); + + $date1 = $test->version; + + $this->assertSame($id, $test->id); + $this->assertInstanceOf('DateTimeImmutable', $date1); + + $test->title = 'changed'; + $this->dm->flush(); + + $this->assertNotSame($date1, $test->version); + + return $test; + } + + public function testLockDateThrowsException() { - $article = new LockTimestamp('Test LockInt'); + $article = new LockDate('Test LockInt'); + $this->dm->persist($article); + $this->dm->flush(); + + // Manually change the version so the next code will cause an exception + $this->dm->getDocumentCollection(get_class($article))->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new UTCDateTime(time() * 1000 + 600)]]); + + // Now lets change a property and try and save it again + $article->title = 'ok'; + + $this->expectException(LockException::class); + + $this->dm->flush(); + } + + public function testLockDateImmutableThrowsException() + { + $article = new LockDateImmutable('Test LockInt'); $this->dm->persist($article); $this->dm->flush(); @@ -382,7 +447,7 @@ public function testInvalidLockDocument() public function testInvalidVersionDocument() { $this->expectException(MongoDBException::class); - $this->expectExceptionMessage('Invalid version field type string. Version field must be int or date.'); + $this->expectExceptionMessage('Invalid version field type string. Version field must be int, integer, date or date_immutable.'); $this->dm->getClassMetadata(InvalidVersionDocument::class); } @@ -468,12 +533,19 @@ class LockInt extends AbstractVersionBase } /** @ODM\Document */ -class LockTimestamp extends AbstractVersionBase +class LockDate extends AbstractVersionBase { /** @ODM\Version @ODM\Field(type="date") */ public $version; } +/** @ODM\Document */ +class LockDateImmutable extends AbstractVersionBase +{ + /** @ODM\Version @ODM\Field(type="date_immutable") */ + public $version; +} + /** @ODM\Document */ class InvalidLockDocument {