mirror of
https://github.com/doctrine/mongodb-odm.git
synced 2026-03-23 22:42:15 +01:00
Support symfony UUID (#2826)
* Add new type for Symfony UUIDs * Add UUID type to type class * Don't transform PHP UUIDs again * Support generating Symfony UUID objects in AUTO generator * Deprecate string-based UUID generator * Add documentation around UUIDs * Apply Copilot review feedback * Unify logic in binary uuid type * Apply code review feedback * Undo type conditional change to fix errors * Add documentation around UUID generator deprecation * Link to Symfony UID component * Explain deprecation of UUID generator in docs * Simplify ID generator docblocks * Fix ObjectId check when using auto generation * Use InvalidArgumentException in type
This commit is contained in:
@@ -14,6 +14,11 @@ The `Doctrine\ODM\MongoDB\Id\AbstractIdGenerator` class has been removed. Custom
|
||||
ID generators must implement the `Doctrine\ODM\MongoDB\Id\IdGenerator`
|
||||
interface.
|
||||
|
||||
The `Doctrine\ODM\MongoDB\Id\UuidGenerator` class has been removed. Use a custom
|
||||
generator to generate string UUIDs. For more efficient storage of UUIDs, use the
|
||||
`Doctrine\ODM\MongoDB\Types\BinaryUuidType` type in combination with the
|
||||
`Doctrine\ODM\MongoDB\Id\SymfonyUuidGenerator` generator.
|
||||
|
||||
## Metadata
|
||||
The `Doctrine\ODM\MongoDB\Mapping\ClassMetadata` class has been marked final and
|
||||
will no longer be extendable.
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^10.4",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"symfony/cache": "^5.4 || ^6.0 || ^7.0"
|
||||
"symfony/cache": "^5.4 || ^6.0 || ^7.0",
|
||||
"symfony/uid": "^5.4 || ^6.0 || ^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.12 || >=3.0"
|
||||
|
||||
@@ -151,6 +151,7 @@ Here is a quick overview of the built-in mapping types:
|
||||
- ``raw``
|
||||
- ``string``
|
||||
- ``timestamp``
|
||||
- ``uuid``
|
||||
|
||||
You can read more about the available MongoDB types on `php.net <https://www.php.net/mongodb.bson>`_.
|
||||
|
||||
@@ -178,6 +179,7 @@ This list explains some of the less obvious mapping types:
|
||||
- ``id``: string to ObjectId by default, but other formats are possible
|
||||
- ``timestamp``: string to ``MongoDB\BSON\Timestamp``
|
||||
- ``raw``: any type
|
||||
- ``uuid``: `Symfony UID <https://symfony.com/doc/current/components/uid.html>`_ to ``MongoDB\BSON\Binary`` instance with a "uuid" type
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -206,6 +208,7 @@ follows:
|
||||
- ``float``: ``float``
|
||||
- ``int``: ``int``
|
||||
- ``string``: ``string``
|
||||
- ``Symfony\Component\Uid\Uuid``: ``uuid``
|
||||
|
||||
Doctrine can also autoconfigure any backed ``enum`` it encounters: ``type``
|
||||
will be set to ``string`` or ``int``, depending on the enum's backing type,
|
||||
@@ -269,13 +272,23 @@ Here is an example:
|
||||
You can configure custom ID strategies if you don't want to use the default
|
||||
object ID. The available strategies are:
|
||||
|
||||
- ``AUTO`` - Uses the native generated ObjectId.
|
||||
- ``AUTO`` - Automatically generates an ObjectId or Symfony UUID depending on the identifier type.
|
||||
- ``ALNUM`` - Generates an alpha-numeric string (based on an incrementing value).
|
||||
- ``CUSTOM`` - Defers generation to an implementation of ``IdGenerator`` specified in the ``class`` option.
|
||||
- ``INCREMENT`` - Uses another collection to auto increment an integer identifier.
|
||||
- ``UUID`` - Generates a UUID identifier.
|
||||
- ``UUID`` - Generates a UUID identifier (deprecated).
|
||||
- ``NONE`` - Do not generate any identifier. ID must be manually set.
|
||||
|
||||
When using the ``AUTO`` strategy in combination with a UUID identifier, the generator can create UUIDs of type 1, type 4,
|
||||
and type 7 automatically. For all other UUID types, assign the identifier manually in combination with the ``NONE``
|
||||
strategy.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``UUID`` generator is deprecated, as it stores UUIDs as strings. It is recommended to use the ``AUTO`` strategy
|
||||
with a ``uuid`` type identifier field instead. If you need to keep generating string UUIDs, you can use the
|
||||
``CUSTOM`` strategy with your own generator.
|
||||
|
||||
Here is an example how to manually set a string identifier for your documents:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@@ -9,6 +9,8 @@ use MongoDB\BSON\ObjectId;
|
||||
|
||||
/**
|
||||
* AutoGenerator generates a native ObjectId
|
||||
*
|
||||
* @deprecated use ObjectIdGenerator instead
|
||||
*/
|
||||
final class AutoGenerator extends AbstractIdGenerator
|
||||
{
|
||||
|
||||
17
lib/Doctrine/ODM/MongoDB/Id/ObjectIdGenerator.php
Normal file
17
lib/Doctrine/ODM/MongoDB/Id/ObjectIdGenerator.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ODM\MongoDB\Id;
|
||||
|
||||
use Doctrine\ODM\MongoDB\DocumentManager;
|
||||
use MongoDB\BSON\ObjectId;
|
||||
|
||||
/** @internal */
|
||||
final class ObjectIdGenerator extends AbstractIdGenerator
|
||||
{
|
||||
public function generate(DocumentManager $dm, object $document): ObjectId
|
||||
{
|
||||
return new ObjectId();
|
||||
}
|
||||
}
|
||||
39
lib/Doctrine/ODM/MongoDB/Id/SymfonyUuidGenerator.php
Normal file
39
lib/Doctrine/ODM/MongoDB/Id/SymfonyUuidGenerator.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ODM\MongoDB\Id;
|
||||
|
||||
use Doctrine\ODM\MongoDB\DocumentManager;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
use Symfony\Component\Uid\UuidV1;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Uid\UuidV7;
|
||||
|
||||
use function array_values;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
|
||||
/** @internal */
|
||||
final class SymfonyUuidGenerator extends AbstractIdGenerator
|
||||
{
|
||||
private const SUPPORTED_TYPES = [
|
||||
1 => UuidV1::class,
|
||||
4 => UuidV4::class,
|
||||
7 => UuidV7::class,
|
||||
];
|
||||
|
||||
public function __construct(private readonly string $class)
|
||||
{
|
||||
if (! in_array($this->class, self::SUPPORTED_TYPES, true)) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid UUID type "%s". Expected one of: %s.', $this->class, implode(', ', array_values(self::SUPPORTED_TYPES))));
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(DocumentManager $dm, object $document): Uuid
|
||||
{
|
||||
return new $this->class();
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,7 @@ use function str_replace;
|
||||
use function strlen;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Generates UUIDs.
|
||||
*/
|
||||
/** @deprecated without replacement. Use a custom generator or switch to binary UUIDs. */
|
||||
final class UuidGenerator extends AbstractIdGenerator
|
||||
{
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,9 @@ use ReflectionClass;
|
||||
use ReflectionEnum;
|
||||
use ReflectionNamedType;
|
||||
use ReflectionProperty;
|
||||
use Symfony\Component\Uid\UuidV1;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Symfony\Component\Uid\UuidV7;
|
||||
|
||||
use function array_column;
|
||||
use function array_filter;
|
||||
@@ -300,6 +303,8 @@ use function trigger_deprecation;
|
||||
|
||||
/**
|
||||
* UUID means Doctrine will generate a uuid for us.
|
||||
*
|
||||
* @deprecated without replacement. Use a custom generator or switch to binary UUIDs.
|
||||
*/
|
||||
public const GENERATOR_TYPE_UUID = 3;
|
||||
|
||||
@@ -942,6 +947,16 @@ use function trigger_deprecation;
|
||||
return [$this->identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mapping of the identifier field
|
||||
*
|
||||
* @phpstan-return FieldMapping
|
||||
*/
|
||||
public function getIdentifierMapping(): array
|
||||
{
|
||||
return $this->fieldMappings[$this->identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* Since MongoDB only allows exactly one identifier field
|
||||
* this will always return an array with only one value
|
||||
@@ -2391,22 +2406,18 @@ use function trigger_deprecation;
|
||||
}
|
||||
|
||||
$this->generatorOptions = $mapping['options'] ?? [];
|
||||
switch ($this->generatorType) {
|
||||
case self::GENERATOR_TYPE_AUTO:
|
||||
$mapping['type'] = 'id';
|
||||
break;
|
||||
default:
|
||||
if (! empty($this->generatorOptions['type'])) {
|
||||
$mapping['type'] = (string) $this->generatorOptions['type'];
|
||||
} elseif (empty($mapping['type'])) {
|
||||
$mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? Type::INT : Type::CUSTOMID;
|
||||
}
|
||||
if ($this->generatorType !== self::GENERATOR_TYPE_AUTO) {
|
||||
if (! empty($this->generatorOptions['type'])) {
|
||||
$mapping['type'] = (string) $this->generatorOptions['type'];
|
||||
} elseif (empty($mapping['type'])) {
|
||||
$mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? Type::INT : Type::CUSTOMID;
|
||||
}
|
||||
} elseif ($mapping['type'] !== Type::UUID) {
|
||||
$mapping['type'] = Type::ID;
|
||||
}
|
||||
|
||||
unset($this->generatorOptions['type']);
|
||||
}
|
||||
|
||||
if (! isset($mapping['type'])) {
|
||||
} elseif (! isset($mapping['type'])) {
|
||||
// Default to string
|
||||
$mapping['type'] = Type::STRING;
|
||||
}
|
||||
@@ -2798,6 +2809,11 @@ use function trigger_deprecation;
|
||||
}
|
||||
|
||||
switch ($type->getName()) {
|
||||
case UuidV1::class:
|
||||
case UuidV4::class:
|
||||
case UuidV7::class:
|
||||
$mapping['type'] = Type::UUID;
|
||||
break;
|
||||
case DateTime::class:
|
||||
$mapping['type'] = Type::DATE;
|
||||
break;
|
||||
|
||||
@@ -12,15 +12,17 @@ use Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs;
|
||||
use Doctrine\ODM\MongoDB\Event\OnClassMetadataNotFoundEventArgs;
|
||||
use Doctrine\ODM\MongoDB\Events;
|
||||
use Doctrine\ODM\MongoDB\Id\AlnumGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\AutoGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\IdGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\IncrementGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\ObjectIdGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\SymfonyUuidGenerator;
|
||||
use Doctrine\ODM\MongoDB\Id\UuidGenerator;
|
||||
use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
|
||||
use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
|
||||
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
|
||||
use Doctrine\Persistence\Mapping\ReflectionService;
|
||||
use ReflectionException;
|
||||
use ReflectionNamedType;
|
||||
|
||||
use function assert;
|
||||
use function get_class_methods;
|
||||
@@ -186,7 +188,7 @@ final class ClassMetadataFactory extends AbstractClassMetadataFactory implements
|
||||
if ($parent->idGenerator) {
|
||||
$class->setIdGenerator($parent->idGenerator);
|
||||
}
|
||||
} else {
|
||||
} elseif ($class->identifier) {
|
||||
$this->completeIdGeneratorMapping($class);
|
||||
}
|
||||
|
||||
@@ -230,12 +232,36 @@ final class ClassMetadataFactory extends AbstractClassMetadataFactory implements
|
||||
return new ClassMetadata($className);
|
||||
}
|
||||
|
||||
private function generateAutoIdGenerator(ClassMetadata $class): void
|
||||
{
|
||||
$identifierMapping = $class->getIdentifierMapping();
|
||||
switch ($identifierMapping['type']) {
|
||||
case 'id':
|
||||
case 'objectId':
|
||||
$class->setIdGenerator(new ObjectIdGenerator());
|
||||
break;
|
||||
case 'uuid':
|
||||
$reflectionProperty = $class->getReflectionProperty($identifierMapping['fieldName']);
|
||||
if (! $reflectionProperty->getType() instanceof ReflectionNamedType) {
|
||||
throw MappingException::autoIdGeneratorNeedsType($class->name, $identifierMapping['fieldName']);
|
||||
}
|
||||
|
||||
$class->setIdGenerator(new SymfonyUuidGenerator($reflectionProperty->getType()->getName()));
|
||||
break;
|
||||
default:
|
||||
throw MappingException::unsupportedTypeForAutoGenerator(
|
||||
$class->name,
|
||||
$identifierMapping['type'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function completeIdGeneratorMapping(ClassMetadata $class): void
|
||||
{
|
||||
$idGenOptions = $class->generatorOptions;
|
||||
switch ($class->generatorType) {
|
||||
case ClassMetadata::GENERATOR_TYPE_AUTO:
|
||||
$class->setIdGenerator(new AutoGenerator());
|
||||
$this->generateAutoIdGenerator($class);
|
||||
break;
|
||||
case ClassMetadata::GENERATOR_TYPE_INCREMENT:
|
||||
$incrementGenerator = new IncrementGenerator();
|
||||
|
||||
@@ -319,4 +319,22 @@ final class MappingException extends BaseMappingException
|
||||
$className,
|
||||
));
|
||||
}
|
||||
|
||||
public static function unsupportedTypeForAutoGenerator(string $className, string $type): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
'The type "%s" can not be used for auto ID generation in class "%s".',
|
||||
$type,
|
||||
$className,
|
||||
));
|
||||
}
|
||||
|
||||
public static function autoIdGeneratorNeedsType(string $className, string $identifierFieldName): self
|
||||
{
|
||||
return new self(sprintf(
|
||||
'The auto ID generator for class "%s" requires the identifier field "%s" to have a type.',
|
||||
$className,
|
||||
$identifierFieldName,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
77
lib/Doctrine/ODM/MongoDB/Types/BinaryUuidType.php
Normal file
77
lib/Doctrine/ODM/MongoDB/Types/BinaryUuidType.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ODM\MongoDB\Types;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use MongoDB\BSON\Binary;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
use function get_debug_type;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
class BinaryUuidType extends Type
|
||||
{
|
||||
public function convertToDatabaseValue(mixed $value): ?Binary
|
||||
{
|
||||
return match (true) {
|
||||
$value === null => null,
|
||||
$value instanceof Binary => $value,
|
||||
$value instanceof Uuid => new Binary($value->toBinary(), Binary::TYPE_UUID),
|
||||
is_string($value) => new Binary(Uuid::fromString($value)->toBinary(), Binary::TYPE_UUID),
|
||||
default => throw new InvalidArgumentException(sprintf('Invalid data type %s received for UUID', get_debug_type($value))),
|
||||
};
|
||||
}
|
||||
|
||||
public function convertToPHPValue(mixed $value): Uuid
|
||||
{
|
||||
if ($value instanceof Uuid) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (! $value instanceof Binary) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid data of type "%s" received for Uuid', get_debug_type($value)));
|
||||
}
|
||||
|
||||
if ($value->getType() !== Binary::TYPE_UUID) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid binary data of type %d received for Uuid', $value->getType()));
|
||||
}
|
||||
|
||||
return Uuid::fromBinary($value->getData());
|
||||
}
|
||||
|
||||
public function closureToMongo(): string
|
||||
{
|
||||
return <<<'PHP'
|
||||
$return = match (true) {
|
||||
$value === null => null,
|
||||
$value instanceof \MongoDB\BSON\Binary => $value,
|
||||
$value instanceof \Symfony\Component\Uid\Uuid => new \MongoDB\BSON\Binary($value->toBinary(), \MongoDB\BSON\Binary::TYPE_UUID),
|
||||
is_string($value) => new \MongoDB\BSON\Binary(\Symfony\Component\Uid\Uuid::fromString($value)->toBinary(), \MongoDB\BSON\Binary::TYPE_UUID),
|
||||
default => throw new \InvalidArgumentException(sprintf('Invalid data type %s received for UUID', get_debug_type($value))),
|
||||
};
|
||||
PHP;
|
||||
}
|
||||
|
||||
public function closureToPHP(): string
|
||||
{
|
||||
return <<<'PHP'
|
||||
if ($value instanceof \Symfony\Component\Uid\Uuid) {
|
||||
$return = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $value instanceof \MongoDB\BSON\Binary) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid data of type "%s" received for Uuid', get_debug_type($value)));
|
||||
}
|
||||
|
||||
if ($value->getType() !== \MongoDB\BSON\Binary::TYPE_UUID) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid binary data of type %d received for Uuid', $value->getType()));
|
||||
}
|
||||
|
||||
$return = \Symfony\Component\Uid\Uuid::fromBinary($value->getData());
|
||||
PHP;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use Doctrine\ODM\MongoDB\Mapping\MappingException;
|
||||
use Doctrine\ODM\MongoDB\Types;
|
||||
use InvalidArgumentException;
|
||||
use MongoDB\BSON\ObjectId;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
|
||||
use function end;
|
||||
use function explode;
|
||||
@@ -45,6 +46,7 @@ abstract class Type
|
||||
public const OBJECTID = 'object_id';
|
||||
public const RAW = 'raw';
|
||||
public const DECIMAL128 = 'decimal128';
|
||||
public const UUID = 'uuid';
|
||||
|
||||
/** @deprecated const was deprecated in doctrine/mongodb-odm 2.1 and will be removed in 3.0. Use Type::INT instead */
|
||||
public const INTID = 'int_id';
|
||||
@@ -86,6 +88,7 @@ abstract class Type
|
||||
self::OBJECTID => Types\ObjectIdType::class,
|
||||
self::RAW => Types\RawType::class,
|
||||
self::DECIMAL128 => Types\Decimal128Type::class,
|
||||
self::UUID => Types\BinaryUuidType::class,
|
||||
];
|
||||
|
||||
/** Prevent instantiation and force use of the factory method. */
|
||||
@@ -167,11 +170,15 @@ abstract class Type
|
||||
{
|
||||
if (is_object($variable)) {
|
||||
if ($variable instanceof DateTimeInterface) {
|
||||
return self::getType('date');
|
||||
return self::getType(self::DATE);
|
||||
}
|
||||
|
||||
if ($variable instanceof ObjectId) {
|
||||
return self::getType('id');
|
||||
return self::getType(self::ID);
|
||||
}
|
||||
|
||||
if ($variable instanceof Uuid) {
|
||||
return self::getType(self::UUID);
|
||||
}
|
||||
} else {
|
||||
$type = gettype($variable);
|
||||
|
||||
@@ -1139,7 +1139,7 @@ final class UnitOfWork implements PropertyChangedListener
|
||||
));
|
||||
}
|
||||
|
||||
if ($class->generatorType === ClassMetadata::GENERATOR_TYPE_AUTO && $idValue !== null && ! preg_match('#^[0-9a-f]{24}$#', (string) $idValue)) {
|
||||
if ($class->getIdentifierMapping()['type'] === Type::ID && $idValue !== null && $class->generatorType === ClassMetadata::GENERATOR_TYPE_AUTO && ! preg_match('#^[0-9a-f]{24}$#', (string) $idValue)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'%s uses AUTO identifier generation strategy but provided identifier is not a valid ObjectId.',
|
||||
$document::class,
|
||||
|
||||
@@ -882,6 +882,12 @@ parameters:
|
||||
count: 1
|
||||
path: lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadataFactory\:\:generateAutoIdGenerator\(\) has parameter \$class with generic class Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
count: 1
|
||||
path: lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php
|
||||
|
||||
-
|
||||
message: '#^Method Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadataFactory\:\:initializeReflection\(\) has parameter \$class with generic class Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata but does not specify its types\: T$#'
|
||||
identifier: missingType.generics
|
||||
@@ -2106,6 +2112,18 @@ parameters:
|
||||
count: 1
|
||||
path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php
|
||||
|
||||
-
|
||||
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertSame\(\) with arguments MongoDB\\BSON\\Binary, null and ''Binary UUIDs are…'' will always evaluate to false\.$#'
|
||||
identifier: staticMethod.impossibleType
|
||||
count: 1
|
||||
path: tests/Doctrine/ODM/MongoDB/Tests/Types/BinaryUuidTypeTest.php
|
||||
|
||||
-
|
||||
message: '#^Call to static method PHPUnit\\Framework\\Assert\:\:assertSame\(\) with arguments Symfony\\Component\\Uid\\UuidV4, null and ''Uuid objects are…'' will always evaluate to false\.$#'
|
||||
identifier: staticMethod.impossibleType
|
||||
count: 1
|
||||
path: tests/Doctrine/ODM/MongoDB/Tests/Types/BinaryUuidTypeTest.php
|
||||
|
||||
-
|
||||
message: '#^Property Doctrine\\ODM\\MongoDB\\Tests\\ArrayTest\:\:\$id is unused\.$#'
|
||||
identifier: property.unused
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ODM\MongoDB\Tests\Functional;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Doctrine\ODM\MongoDB\Tests\BaseTestCase;
|
||||
use Doctrine\ODM\MongoDB\Types\Type;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
|
||||
class UuidMappingTest extends BaseTestCase
|
||||
{
|
||||
public function testIdHasUuidType(): void
|
||||
{
|
||||
$metadata = $this->dm->getClassMetadata(UuidTestDocument::class);
|
||||
$idMapping = $metadata->getIdentifierMapping();
|
||||
self::assertSame(Type::UUID, $idMapping['type'], 'Id field should have UUID type');
|
||||
}
|
||||
|
||||
public function testExplicitValue(): void
|
||||
{
|
||||
$uuid = new UuidV4();
|
||||
$document = new UuidTestDocument();
|
||||
|
||||
$document->id = $uuid;
|
||||
$document->explicitlyTypedUuid = $uuid;
|
||||
$document->untypedUuid = $uuid;
|
||||
|
||||
$this->dm->persist($document);
|
||||
$this->dm->flush();
|
||||
|
||||
$check = $this->dm->find(UuidTestDocument::class, $document->id);
|
||||
self::assertInstanceOf(UuidTestDocument::class, $check);
|
||||
self::assertEquals($uuid, $check->id);
|
||||
self::assertEquals($uuid, $check->explicitlyTypedUuid);
|
||||
self::assertEquals($uuid, $check->untypedUuid);
|
||||
}
|
||||
|
||||
public function testAutoGenerateIdV4(): void
|
||||
{
|
||||
$document = new UuidTestDocument();
|
||||
|
||||
$this->dm->persist($document);
|
||||
$this->dm->flush();
|
||||
|
||||
$check = $this->dm->find(UuidTestDocument::class, $document->id);
|
||||
self::assertInstanceOf(UuidTestDocument::class, $check);
|
||||
self::assertInstanceOf(UuidV4::class, $check->id);
|
||||
}
|
||||
}
|
||||
|
||||
#[ODM\Document]
|
||||
class UuidTestDocument
|
||||
{
|
||||
#[ODM\Id]
|
||||
public UuidV4 $id;
|
||||
|
||||
#[ODM\Field(type: Type::UUID)]
|
||||
public ?UuidV4 $explicitlyTypedUuid = null;
|
||||
|
||||
#[ODM\Field]
|
||||
public ?UuidV4 $untypedUuid = null;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ODM\MongoDB\Tests\Types;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Types\Type;
|
||||
use InvalidArgumentException;
|
||||
use MongoDB\BSON\Binary;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
use Throwable;
|
||||
|
||||
class BinaryUuidTypeTest extends TestCase
|
||||
{
|
||||
public function testConvertToDatabaseValue(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
$uuid = new UuidV4();
|
||||
$stringUuid = $uuid->toRfc4122();
|
||||
$binaryUuid = new Binary($uuid->toBinary(), Binary::TYPE_UUID);
|
||||
|
||||
self::assertNull($type->convertToDatabaseValue(null), 'null is not converted');
|
||||
self::assertEquals($binaryUuid, $type->convertToDatabaseValue($uuid), 'Uuid objects are converted to Binary objects');
|
||||
self::assertEquals($binaryUuid, $type->convertToDatabaseValue($stringUuid), 'String UUIDs are converted to Binary objects');
|
||||
self::assertSame($binaryUuid, $type->convertToDatabaseValue($binaryUuid), 'Binary UUIDs are returned as is');
|
||||
}
|
||||
|
||||
public function testConvertInvalidUuid(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$type->convertToDatabaseValue('invalid');
|
||||
}
|
||||
|
||||
public function testConvertToPHPValue(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
$uuid = new UuidV4();
|
||||
$binaryUuid = new Binary($uuid->toBinary(), Binary::TYPE_UUID);
|
||||
|
||||
self::assertEquals($uuid, $type->convertToPHPValue($binaryUuid), 'Binary UUIDs are converted to Uuid objects');
|
||||
self::assertSame($uuid, $type->convertToPHPValue($uuid), 'Uuid objects are returned as is');
|
||||
}
|
||||
|
||||
public function testConvertInvalidBinaryUuid(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$type->convertToPHPValue(new Binary('invalid', Binary::TYPE_UUID));
|
||||
}
|
||||
|
||||
public function testConvertInvalidBinary(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
|
||||
$this->expectException(Throwable::class);
|
||||
$type->convertToPHPValue(new Binary('invalid', Binary::TYPE_GENERIC));
|
||||
}
|
||||
|
||||
public function testClosureToMongo(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
$uuid = new UuidV4();
|
||||
$stringUuid = $uuid->toRfc4122();
|
||||
$binaryUuid = new Binary($uuid->toBinary(), Binary::TYPE_UUID);
|
||||
|
||||
$convertToDatabaseValue = static function ($value) use ($type) {
|
||||
$return = null;
|
||||
eval($type->closureToMongo());
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
self::assertNull($convertToDatabaseValue(null), 'null is not converted');
|
||||
self::assertEquals($binaryUuid, $convertToDatabaseValue($uuid), 'Uuid objects are converted to Binary objects');
|
||||
self::assertEquals($binaryUuid, $convertToDatabaseValue($stringUuid), 'String UUIDs are converted to Binary objects');
|
||||
self::assertSame($binaryUuid, $convertToDatabaseValue($binaryUuid), 'Binary UUIDs are returned as is');
|
||||
}
|
||||
|
||||
public function testClosureToPhp(): void
|
||||
{
|
||||
$type = Type::getType(Type::UUID);
|
||||
$uuid = new UuidV4();
|
||||
$binaryUuid = new Binary($uuid->toBinary(), Binary::TYPE_UUID);
|
||||
|
||||
$convertToPHPValue = static function ($value) use ($type) {
|
||||
$return = null;
|
||||
eval($type->closureToPHP());
|
||||
|
||||
return $return;
|
||||
};
|
||||
|
||||
self::assertEquals($uuid, $convertToPHPValue($binaryUuid), 'Binary UUIDs are converted to Uuid objects');
|
||||
self::assertSame($uuid, $convertToPHPValue($uuid), 'Uuid objects are returned as is');
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@ use MongoDB\BSON\ObjectId;
|
||||
use MongoDB\BSON\Timestamp;
|
||||
use MongoDB\BSON\UTCDateTime;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use Symfony\Component\Uid\UuidV4;
|
||||
|
||||
use function get_debug_type;
|
||||
use function hex2bin;
|
||||
use function md5;
|
||||
use function str_pad;
|
||||
use function str_repeat;
|
||||
@@ -66,6 +68,7 @@ class TypeTest extends BaseTestCase
|
||||
'objectId' => [Type::OBJECTID, '507f1f77bcf86cd799439011', new ObjectId('507f1f77bcf86cd799439011')],
|
||||
'raw' => [Type::RAW, (object) ['foo' => 'bar']],
|
||||
'decimal128' => [Type::DECIMAL128, '4.20', new Decimal128('4.20')],
|
||||
'uuid' => [Type::UUID, new UuidV4('550e8400-e29b-41d4-a716-446655440000'), new Binary(hex2bin('550e8400e29b41d4a716446655440000'), Binary::TYPE_UUID)],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user