Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The DTO process fails if used with embedded input objects #4610

Closed
rimas-kudelis opened this issue Dec 28, 2021 · 1 comment
Closed

The DTO process fails if used with embedded input objects #4610

rimas-kudelis opened this issue Dec 28, 2021 · 1 comment

Comments

@rimas-kudelis
Copy link
Contributor

rimas-kudelis commented Dec 28, 2021

API Platform version(s) affected: 2.5.10

Description
I'm trying to make my API use DTOs for both input and output and I want to reuse same classes for both input and output.

This works fine for input and output of objects without embedded objects. Also works fine for output of embedded objects: both the "parent" object and the "child" object are being transformed to DTOs before output to JSON.

However, it breaks with input of embedded objects: even though I specify an input class for a resource which I'm sending embedded, a denormalizer for that DTO class is never being called, and neither is the DTO to Entity transformer. Instead, API Platform attempts to denormalize my input directly into the Entity class, creates an entity full of null values, and then causes an exception when trying to save that very very broken value into the database.

After tracing what happens, I think this is due to how AbstractItemNormalizer works: I discovered that it actually does make a call to my child-DTO-to-Entity-transformer's supportsTransformation() method, but passes no information about the DTO class, from which the ability to transform is being queried. This causes my transformer to return false and, as a result, no transformer that is able to transform the DTO to Entity is being found, so the whole Denormalize To DTO and Transform logic, which is applied to DTOs, is being skipped, and instead an attempt is made to denormalize the array straight into the Entity, which ends up producing a blank Entity which can not and should not be saved.

Same issue seems to be happening with Api Platform 2.6.

How to reproduce
I went through the API Platform course material on SymfonyCasts, and updated the project I produced along the way with a scenario to reproduce the problem. The result is available in this branch. An easy way to see it live is to send a POST request to /api/users with the following JSON body:

{
    "email": "cheesefan22@example.com",
    "username": "cheesefan22",
    "password": "123",
    "isMe": true,
    "isMvp": true,
    "cheeseListings": [
      {
        "title": "Mysterious munster",
        "price": 1500
      },
      {
        "title": "Block of cheddar the size of your face!",
        "price": 5000
      }
   ]
}

The crucial part here is, of course, the cheeseListings array. When I send this request, I get a 500 response caused by a Doctrine Integrity constraint violation (which says that null was supplied as title). Furthermore, I've added a couple debug dumps which are shown in the profiler's Debug tab, where it's clearly visible what arguments were passed to the input DataTransformer in question and what it returned. Interestingly, both $context['input'] and $context['output'] are passed as null:
screenshot

Additional Context
I think there might be a somewhat deeper problem with how supportsTransformation() is being called: right now it has to sort out what is being checked from both the context and the data that is being passed. For example, for DTO→Entity transformers, the data passed might be an array while in reality the ability to transform from DTO is being checked (like in this case). Furthermore, at least in this case $context['input'] is undefined, and $context['output'] holds a class name of the parent object, which is hardly relevant to the child object transformer.

Even the example in the official documentation looks somewhat weird, because it only suggests checking that the value of $to is correct and that $context['input']['class'] is not null, thus encouraging a blind assumption that the actual input will be acceptable if output class is what we expect. This actually makes me wonder why the not null check is even there.

Perhaps supportsTransformation() should accept an additional argument which would specify object class or scalar type of the original data? I reckon that most transformers could just check that value in combination with $to instead of looking at actual data.

Alternatively, perhaps $data could be made more useful than it is now. What's the point of passing an unserialized JSON array there, if you're checking support for transfroming from a DTO?

rimas-kudelis pushed a commit to rimas-kudelis/symfonycatsts-api-platform-tutorial that referenced this issue Dec 28, 2021
@soyuka
Copy link
Member

soyuka commented Sep 16, 2022

Try API Platform 2.7/3.0 and let me know if you have issues.

@soyuka soyuka closed this as completed Sep 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants