diff --git a/docs/en/reference/transactions-and-concurrency.rst b/docs/en/reference/transactions-and-concurrency.rst index 97cd2f32df..dae01691f2 100644 --- a/docs/en/reference/transactions-and-concurrency.rst +++ b/docs/en/reference/transactions-and-concurrency.rst @@ -105,7 +105,7 @@ a ``LockException`` is thrown, which indicates that the document was already mod .. note:: Only types implementing the ``\Doctrine\ODM\MongoDB\Types\Versionable`` interface can be used for versioning. - Following ODM types can be used for versioning: ``int``, ``decimal128``, ``date``, and ``date_immutable``. + Following ODM types can be used for versioning: ``int``, ``decimal128``, ``date``, ``date_immutable``, and ``object_id``. Document Configuration ^^^^^^^^^^^^^^^^^^^^^^ @@ -190,7 +190,8 @@ Choosing the Field Type """"""""""""""""""""""" When using the date-based type in a high-concurrency environment, it is still possible to create multiple documents -with the same version and cause a conflict. This can be avoided by using the ``int`` or ``decimal128`` type. +with the same version and cause a conflict. This can be avoided by using the ``int``, ``decimal128``, or ``object_id`` type. +The ``object_id`` type contains the timestamp of its creation, but also a random value to ensure uniqueness. Usage """"" diff --git a/lib/Doctrine/ODM/MongoDB/Types/ObjectIdType.php b/lib/Doctrine/ODM/MongoDB/Types/ObjectIdType.php index 13f7f87ffc..9a96a8859a 100644 --- a/lib/Doctrine/ODM/MongoDB/Types/ObjectIdType.php +++ b/lib/Doctrine/ODM/MongoDB/Types/ObjectIdType.php @@ -9,7 +9,7 @@ /** * The ObjectId type. */ -class ObjectIdType extends Type +class ObjectIdType extends Type implements Versionable { public function convertToDatabaseValue($value) { @@ -38,4 +38,9 @@ public function closureToPHP(): string { return '$return = (string) $value;'; } + + public function getNextVersion($current): ObjectId + { + return new ObjectId(); + } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Types/VersionableTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Types/VersionableTest.php new file mode 100644 index 0000000000..ba48765092 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Types/VersionableTest.php @@ -0,0 +1,83 @@ +getType(Type::INT); + self::assertSame(1, $type->getNextVersion(null)); + self::assertSame(2, $type->getNextVersion(1)); + } + + public function testDecimal128NextVersion(): void + { + $type = $this->getType(Type::DECIMAL128); + self::assertSame('1', $type->getNextVersion(null)); + self::assertSame('2', $type->getNextVersion('1')); + } + + public function testDateTimeNextVersion(): void + { + $type = $this->getType(Type::DATE); + $current = new DateTime(); + $next = $type->getNextVersion(null); + self::assertInstanceOf(DateTime::class, $next); + self::assertGreaterThanOrEqual($current, $next); + self::assertLessThanOrEqual(new DateTime(), $next); + + $next = $type->getNextVersion(new DateTime('2000-01-01')); + self::assertInstanceOf(DateTime::class, $next); + self::assertGreaterThanOrEqual($current, $next); + self::assertLessThanOrEqual(new DateTime(), $next); + } + + public function testDateTimeImmutableNextVersion(): void + { + $type = $this->getType(Type::DATE_IMMUTABLE); + $current = new DateTime(); + $next = $type->getNextVersion(null); + self::assertInstanceOf(DateTimeImmutable::class, $next); + self::assertGreaterThanOrEqual($current, $next); + self::assertLessThanOrEqual(new DateTimeImmutable(), $next); + + $next = $type->getNextVersion(new DateTimeImmutable('2000-01-01')); + self::assertInstanceOf(DateTimeImmutable::class, $next); + self::assertGreaterThanOrEqual($current, $next); + self::assertLessThanOrEqual(new DateTimeImmutable(), $next); + } + + public function testObjectIdNextVersion(): void + { + $type = $this->getType(Type::OBJECTID); + $current = new ObjectId(); + $next = $type->getNextVersion(null); + self::assertInstanceOf(ObjectId::class, $next); + self::assertGreaterThan($current, $next); + self::assertLessThan(new ObjectId(), $next); + + $next = $type->getNextVersion($current); + self::assertInstanceOf(ObjectId::class, $next); + self::assertGreaterThan($current, $next); + self::assertLessThan(new ObjectId(), $next); + } + + private function getType(string $name): Versionable + { + $type = Type::getType($name); + + self::assertInstanceOf(Versionable::class, $type); + + return $type; + } +}