-
-
Notifications
You must be signed in to change notification settings - Fork 850
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
Do not try do retrieve object in ItemNormalizer $context['resource_class'] is not defined #2326
Conversation
I'm not sure I understand the problem here. The |
It's even explicitly set here: core/src/Serializer/AbstractItemNormalizer.php Lines 125 to 127 in 516c9c7
|
I guess you can simply try to decode some data with an but basically this condition evaluate to core/src/Serializer/ItemNormalizer.php Line 35 in 516c9c7
so it jumps in updateObjectToPopulate , an InvalidArgumentException exception is thrown, and finally this line breaks everything:core/src/Serializer/ItemNormalizer.php Line 52 in 516c9c7
Did I miss something? |
Can't you give a context? What's your use case? |
@soyuka sure, it's already explained in the PR description. |
Normalizers must work even if no context is provided. The fix looks good to me. Would you mind adding an unit test to prevent regressions @lyrixx? |
@dunglas Sure 👍 I will have to clone the projet locally then 😂 |
src/Serializer/ItemNormalizer.php
Outdated
@@ -32,7 +32,7 @@ class ItemNormalizer extends AbstractItemNormalizer | |||
public function denormalize($data, $class, $format = null, array $context = []) | |||
{ | |||
// Avoid issues with proxies if we populated the object | |||
if (isset($data['id']) && !isset($context[self::OBJECT_TO_POPULATE])) { | |||
if (isset($data['id']) && !isset($context[self::OBJECT_TO_POPULATE]) && isset($context['resource_class'])) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe that a better fix would be to resolve this value if not provided by calling ResourceClassResolver::getResourceClass()
? Something like $context['resource_class'] ?? $this->resourceClassResolver($data)
in updateObjectToPopulate()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree. If it's not provided, it means we are outside the nominal api platform workflow.
We should not guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could expect from a denormalizer that it updates relations by default, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You lost me. Sorry. If we set $context['resource_class']
, the code will still raise an exception. Here I don't want to fetch something via the iriConverter
.
Or you can just run |
@dunglas I made the initial patch on github ;) But don't worry, I know how to update a patch |
As I've already mentioned, the @dunglas I agree we should set |
@teohhanhui Heuuuuu, I don't get it 1.crearte a new
Since API Platform modify the default serialization, you can not say:
|
I have added an unit test |
Can you open a PR with a failing Behat test? Because
Need more context. Is it inside an API Platform operation? Are you doing it through a data provider? |
Sure, it's quite simple: lyrixx/test@085fe66 To make things crystal clear: we use the same object for the "public" API and for Elasticsearch storage |
If you declare a class as an API Resource, it's normal that our
In your case, you just want to use Symfony's default |
@teohhanhui Yes. And other Normalizers that could live in my application. |
Please bear with us in trying to figure out the best way to go about it. So far I'm not convinced that it should be done this way. I'm inclined more towards @dunglas' suggestion of setting As for your specific use case:
|
I did not choose to use it, it's build in with API Platform. |
Okay, I think I get it now. And it should be a supported use case, yes. The question is how? 🤔 |
@lyrixx A possible workaround could be to pass a custom |
In my code, or in ApiPlatform ? |
In your code. 😄 |
For now I used a very simple workaround $this->serializer->denormalize($logResult['_source'], Log::class, 'json', [
'object_to_populate' => $log = new Log(),
'groups' => ['elasticsearch'],
]); |
The patch is good enough for now, it doesn't break anything, and prevent an error that must be handled anyway. Could you just rebase against 2.3 please? (because it's a bug fix) |
@dunglas I think it's the wrong fix. As we've discussed, |
Maybe something like this?
This patch also feels wrong to me, on the other hand it doesn't harm so I'm mitigated :p. |
Can we merge this until we find a better fix @api-platform/core-team? Having a hard failure is worst... |
If we log something because it's not normal as I suggested (#2326 (comment)) I'm 👍 to merge. |
…ass'] is not defined We store some document in Elasticsearch and we hydrate them with the Serializer. But the `ItemNormalizer` raise an error because the `resource_class` is not defined.
I added a log line |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still think it's the wrong fix. The correct fix should be to set $context['resource_class']
when it's missing.
Ok, Let's try what you suggest to prove it does not work: If I set
Legit, so I will have to implement a At some point you said:
Well:
You also said:
So your suggestion, in order to not merge this PR, is that I need to duplicate all my model class to get one form API Platform, and another one for ES <-> PHP, and a layer that will my map my two model ? I'm sorry, but you are making things really difficult. You ask for things, I did them, then you refused it again. It's really not king. Thus, I will tell you what is the real fix could be: 1/ Update Symfony with: interface NormalizerInterface
{
- public function supportsNormalization($data, $format = null);
+ public function supportsNormalization($data, $format = null, array $context = array());
} 2/ update |
No. I recognize that your use case is a valid one and it's unfortunate that it's not working at the moment (but you have found workarounds). What I'm saying is that it's bad for us to have such workarounds in our code. |
It's already supported in Symfony: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/ContextAwareNormalizerInterface.php#L26
I don't agree with this one, it's totally valid to use API Platform to serialize in JSON-LD outside of an API context (in a worker, to send data to a Kafka, an Elastic...).
This specific problem can be fixed like this: https://api-platform.com/docs/core/data-providers/#injecting-the-serializer-in-an-code-classlanguage-textitemdataprovidercode It's a hack, and the good solution would be to use a specific Serializer instance in API Platform (not the same one than the default serializer of Symfony). It would be cleaner, but it requires some more work in SF (creating a factory to build properly configured serializer services). I'll do it when I'll have some free time. In the meantime, I in favor of merging @lyrixx patch. We should never throw an unhandled PHP error, it's not robust code... |
Agree. But this patch is not doing the right thing. Setting |
About the point 3, I'm not talking about a DIC circular reference, |
@teohhanhui I don't understand in your solution how can someone deserialize something that must not use ApiPlatform, our process is very simple: Fetch Data From Elastisearch -> Deserialize it (we don't want API Platform here, it's just a stupid object) -> Do Some Work -> Serialize It (with api platform) With what your are proposing the only solution would be to use another Serializer, I would be fine with that if API Platform did not replace the one from Symfony. In our use case we have installed API Platform after doing the deserialization with Elasticsearch, and changing a lot of our code and DIC for that may be painful for some users. IMO A viable solution would be to change the way API Platform is injected :
|
@dunglas #2399 Functionally it works but this is a major waste of resource:
As I said many time, I really thing this patch is correct, and we should not try to goes through API Platform Normalizer at all cost. There are legitimate case where we don't want IRI stuff. Here is one example. And again, I know we could create another serializer + normalizers but it's really boring to have to put many lines of services configuration in my project, where a simple if could solve everything. On some "page" of our application, we retrieve 100 objects. So there will be 100 Exception + 100 |
Let's merge |
Thanks @lyrixx |
Thanks you |
We store some document in Elasticsearch and we hydrate them with the Serializer.
But the
ItemNormalizer
raise an error because theresource_class
is not defined.