Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[RFC] ObjectMapper #54476

Copy link
Copy link
Open
Open
Copy link
@Korbeil

Description

@Korbeil
Issue body actions

Hey everyone 👋

This issue is here to standardize how we should think about mapping in future and a proposition on how we could harmonize all theses ideas in one component.

Context

Today we have some PR that are about mapping and we all have ideas on how we should do things. And we probably also already have use-case for all those ideas.

Currently when you think about mapping (object to object mapping, atleast), you will require to use the Serializer not exactly as it was designed. You'll need to normalize your source object then denormalize to the targeted object.

This is a bit complex for what we want to do and it brings some limitations:

  • We will have an intermediate array state, the bigger the source object is, the bigger the intermediate array will be;
  • If we want to customize some part of that mapping, we will need to write a Normalizer/Denormalizer for the whole transformation.

Due to these limitations, we think that there is a need for a new component that will map objects.

Current implementations

Let's list the current two main implementations:

Both theses implementations are trying to tackle object to object mapping. Indeed we have some different features because of how we implemented things:

  • @soyuka's PR focus on DX & simple implementation. The code is really easy to understand and it will do object to object mapping really well. It's mostly based onto PropertyAccessor component.
  • jolicode/automapper is focused on performance and can map a bit more than from an object to an object. It's more complex due to AST usage and is mostly based on PropertyInfo component (which will switch on TypeInfo once 7.1 is released)

Future

We talked with some core contributors during SymfonyLive Paris 2024 (28/29 march) and with @nicolas-grekas we came to the conclusion that we won't find a perfect implementation to merge as it is and we should iterate on all theses ideas.

In my opinion, the component should be focused on one main interface:

interface ObjectMapperInterface 
{
    /**
     * @template T
     * @template K
     * 
     * @param array|\stdClass|T $source
     * @param class-string<K>|'array'|K $target
     * @return array|K
     */
    public function map(array|object $source, string|object $target, array $context = []): array|object;
}

This interface will handle object to object mapping and array to object (or inverse) mapping.
I added array to object mapping (or inverse) because I do think we should it for some cases like handling a Request query parameters of to handle mapping Form values to an object.

For now, best thing would be to merge @soyuka's PR and iterate from that point towards theses interfaces with both implementations. Having two implementations isn't a big issue, we already have components working that way like DependencyInjection or ExpressionLanguage. Here the goal is to have two way to map objects:

  • Using reflection
  • Generating code

Using reflection makes the code way simpler and generating code is more complex but gives amazing performance.
Once the ObjectMapper PR is merged we need to make a wide and very good test suite so we can start adding code generating and validate we have same behavior with both implementations.

That way we can use any implementation whenever one fit better than the other for our use-case.

Metadata

Here is a section about metadata management within the ObjectMapper, I'll try to explain how metadata retrieval should work. There is 3 ways to collect metadata: from the source, from the target or from both.

We will fetch metadata from the source or from the target only when the source or target is either an array or a \stdClass. We will always fetch metadata from the non-array (or stdClass) object and try to match similar fields within the array or stdClass. When mapping to an array or stdClass, it's easy since all the fields are to create so there won't be matching issues. But when mapping from an array or stdClass, we will need to check if the fields does match properties in the target object, when it's matching we will map but if it's not matching we won't map that field.

Mapping an array to array (or stdClass) should throw an exception and is not possible.

Then, last method is getting metadata from both the source and target objects:
When doing that we will fetch all properties to map from both objects and if one of the source object property matches one of the target property, we will map that field. A property can match another property if its name match in both objects (or same configured name, if MapTo/MapFrom brings some new name).

Conclusion

I really do think ObjectMapper is an awesome component for Symfony. Today it's complex to improve the Serializer and I do think the "next" Serializer will be coming from another component or another set of component.
With the RFC as it is, we could replace the Normalization part of the Serializer.

In addition to component like @mtarld's JsonEncoder it could make a complete serialization replacement like Mathias & I showed at SymfonyLive 2024 with the TurboSerializer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFCRFC = Request For Comments (proposals about features that you want to be discussed)RFC = Request For Comments (proposals about features that you want to be discussed)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.