Skip to content

Conversation

GromNaN
Copy link
Member

@GromNaN GromNaN commented Sep 1, 2025

Q A
Type feature
BC Break no
Fixed issues Related to #2789

Summary

The method getTypeFromPHPVariable is used only by convertPHPToDatabaseValue. Which is used to process expressions in the aggregation builder.

With this new feature, we try to find the type for object class name; assuming custom types are registered using the FQCN of the objects they store.

@GromNaN GromNaN added the Feature label Sep 1, 2025

.. code-block:: php

#[Field(type: Uuid::class)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the custom type here should not be necessary. As soon as you register a type with the value object type name, it should be registered automatically

switch ($type->getName()) {
case DateTime::class:
$mapping['type'] = Type::DATE;
break;
case DateTimeImmutable::class:
$mapping['type'] = Type::DATE_IMMUTABLE;
break;
case 'array':
$mapping['type'] = Type::HASH;
break;
case 'bool':
$mapping['type'] = Type::BOOL;
break;
case 'float':
$mapping['type'] = Type::FLOAT;
break;
case 'int':
$mapping['type'] = Type::INT;
break;
case 'string':
$mapping['type'] = Type::STRING;
break;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it clear from this example that you're referencing Ramsey\Uuid\Uuid? There's no use statement and this is a separate code block from the custom type class above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use the FQCN.

@GromNaN GromNaN requested a review from Copilot September 1, 2025 16:26
Copilot

This comment was marked as outdated.

Type::addType(Ramsey\Uuid\Uuid::class, My\Project\Types\UuidType::class);

.. note::
The type name should match the support class name (e.g., ``uuid`` for ``UuidType``) to enable automatic association with the field type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the automatic association, how does that work? Does it take everything before the Type suffix? How does it handle casing, e.g. in FooBarType?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain, but I think this might utilize doctrine/inflector internally.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be helpful to add here what format would constitute a match.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, there is no such magic. The name should be the class name of the value object.


You can create a custom mapping type for your own value objects or classes. For
example, to map a UUID value object using the `ramsey/uuid library`_, you can
implement a type that converts between your class and the MongoDB binary format.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this say "BSON binary format", or do you think that'd just be more confusing for users? OK to leave as-is.

sprintf(
'Could not convert database value "%s" from "%s" to %s',
$value,
get_debug_type($value),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are use function lines necessary? I'm OK to omit them, but just want to ensure you've considered the question.


.. code-block:: php

#[Field(type: Uuid::class)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it clear from this example that you're referencing Ramsey\Uuid\Uuid? There's no use statement and this is a separate code block from the custom type class above.

Type::addType(Ramsey\Uuid\Uuid::class, My\Project\Types\UuidType::class);

.. note::
The type name should match the support class name (e.g., ``uuid`` for ``UuidType``) to enable automatic association with the field type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not certain, but I think this might utilize doctrine/inflector internally.


This pattern can be adapted for any custom class—just implement the conversion logic for your value object.

.. |FQCN| raw:: html <abbr title="Fully-Qualified Class Name">FQCN</abbr>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GromNaN: this looks like it may be a false positive, since this is some very niche RST syntax

.. note::
The type name should match the support class name (e.g., ``uuid`` for ``UuidType``) to enable automatic association with the field type.

This pattern can be adapted for any custom class—just implement the conversion logic for your value object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same feedback here as @paulinevos' thread above re: "class-just"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence was duplicate if the previous one. I removed it.

{
public static function invalidTypeName(string $name): self
{
return new self(sprintf('Invalid type specified "%s".', $name));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use a colon?

Suggested change
return new self(sprintf('Invalid type specified "%s".', $name));
return new self(sprintf('Invalid type specified: "%s".', $name));

'integer' => self::getType('int'),
'boolean' => self::getType('bool'),
'double' => self::getType('float'),
'string' => self::getType('string'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason you're not referencing the constants here, as you did above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason. Updated.

@GromNaN GromNaN force-pushed the type-from-php-variable branch from d446495 to d106a5a Compare September 3, 2025 15:59
Copy link
Member Author

@GromNaN GromNaN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I added the type detection for DTO class name in property type.

Type::addType(Ramsey\Uuid\Uuid::class, My\Project\Types\UuidType::class);

.. note::
The type name should match the support class name (e.g., ``uuid`` for ``UuidType``) to enable automatic association with the field type.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, there is no such magic. The name should be the class name of the value object.

.. note::
The type name should match the support class name (e.g., ``uuid`` for ``UuidType``) to enable automatic association with the field type.

This pattern can be adapted for any custom class—just implement the conversion logic for your value object.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence was duplicate if the previous one. I removed it.


This pattern can be adapted for any custom class—just implement the conversion logic for your value object.

.. |FQCN| raw:: html <abbr title="Fully-Qualified Class Name">FQCN</abbr>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I revert changes on this line. I'm not sure how this will be formatted.


.. code-block:: php

#[Field(type: Uuid::class)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use the FQCN.

'integer' => self::getType('int'),
'boolean' => self::getType('bool'),
'double' => self::getType('float'),
'string' => self::getType('string'),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason. Updated.

Comment on lines +220 to +227
By using the |FQCN| of the value object class as the type name, the type is
automatically used when encountering a property of that class. This means you
can omit the ``type`` option when defining the field mapping::

.. code-block:: php

#[Field]
public Ramsey\Uuid\Uuid $id;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new feature that can compete with embedded documents: adding custom logic to convert a value object into BSON.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a global namespace for exception like it is generally done in libraries. Existing exception names are:

  • Doctrine\ODM\MongoDB\MongoDBException
  • Doctrine\ODM\MongoDB\ConfigurationException
  • Doctrine\ODM\MongoDB\DocumentNotFoundException
  • Doctrine\ODM\MongoDB\Mapping\MappingException
  • Doctrine\ODM\MongoDB\Hydrator\HydratorException
  • Doctrine\ODM\MongoDB\LockException

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves type detection for PHP variables in the MongoDB ODM by enhancing the getTypeFromPHPVariable method to automatically detect custom types based on object class names. The change allows custom types registered using their FQCN to be automatically selected when processing aggregation expressions.

Key changes:

  • Enhanced type detection to check if an object's class name corresponds to a registered custom type
  • Improved exception handling by introducing a dedicated InvalidTypeException
  • Added automatic type inference for object properties when custom types are registered with class FQCNs

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
lib/Doctrine/ODM/MongoDB/Types/Type.php Enhanced getTypeFromPHPVariable method to detect custom types by class name and improved type detection logic
lib/Doctrine/ODM/MongoDB/Types/InvalidTypeException.php New dedicated exception class for invalid type scenarios
lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php Added automatic type detection for properties with registered custom types
tests/Doctrine/ODM/MongoDB/Tests/Types/TypeTest.php Added comprehensive test coverage for the new type detection functionality
tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomTypeTest.php Added tests demonstrating custom type detection and proper test setup/teardown
tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2789Test.php Improved test isolation by properly managing type registry state
docs/en/reference/custom-mapping-types.rst Updated documentation with UUID custom type example and FQCN usage patterns

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants