8000 [Serializer] Unable to deserialize into existing Doctrine entities · Issue #15627 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Serializer] Unable to deserialize into existing Doctrine entities #15627

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

Closed
bwegrzyn opened this issue Aug 26, 2015 · 10 comments
Closed

[Serializer] Unable to deserialize into existing Doctrine entities #15627

bwegrzyn opened this issue Aug 26, 2015 · 10 comments

Comments

@bwegrzyn
Copy link

Doctrine entities are sometimes wrapped in Proxy objects, and the instantiateObject method does not handle them transparently.

For example, an entity MyBundle\Entity\User might have a class name of Proxies\__CG__\MyBundle\Entity\User. When this occurs, the instantiateObject method does not detect that it is in fact the same entity, and attempts to create a new one.

The Security component uses ClassUtils::getRealClass() to get around this issue.

I believe the issue is with the following line of code:
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php#L250

@linaori
Copy link
Contributor
linaori commented Aug 27, 2015

The actual class is located in doctrine. See: https://github.com/symfony/security-acl/pull/3/files#diff-f95db4a0923d2b083e7c0d2d111c722eR46

@dunglas
Copy link
Member
dunglas commented Aug 27, 2015

@iltar the same code is also present in Symfony: https://github.com/symfony/symfony/blob/2.8/src/Symfony/Component/Security/Core/Util/ClassUtils.php#L55-L63

Btw we also have the same code in API Platform for the same reason (Doctrine): https://github.com/dunglas/DunglasApiBundle/blob/dcfee7f57e0fba8d85db214f2993bcf37a910209/Util/ClassInfoTrait.php#L30-L33

I'm not fond of adding class_exists('Doctrine\Common\Util\ClassUtils') everywhere but it's the easier way to fix this bug. What do you think @symfony/deciders?

@linaori
Copy link
Contributor
linaori commented Aug 27, 2015

@dunglas I'm actually moving it from there to the ACL, because it's not used anywhere in the security component but the ACL.

My idea was to create an injectable class that arranges this. The doctrine bridge can hook in on that and create a custom implementation that can resolve proxy names to entity names, which would make it re-usable here.

Once in the doctrine bridge, the doctrine bundle can add a "resolver" of some sort to the resolver service (ClassNameResolver?). This way there is a default service available in Symfony to handle this.

@dunglas
Copy link
Member
dunglas commented Aug 27, 2015

@iltar Potentially I think that this problem can also occur when using @Ocramius' Proxy Manager.

@Ocramius
Copy link
Contributor

If the proxy class exists, then you should be actually able to deserialize back into into a proxy. Why do you want a cast?

@bwegrzyn
Copy link
8000 Author

@dunglas I actually found this issue while using DunglasApiBundle. ItemNormalizer successfully overrides the class name to deserialize when it finds the object via @id, but when the object is found by the ResourceController, and passed in via object_to_populate it fails because of this bug in the serializer component.

I think you got rid of the controller after beta 3 so I'm not sure if this still applies, but that's how I stumbled across this.

@Ocramius The idea is to deserialize back in to the proxy. The serializer does not handle this correctly because the proxy class name does not match the expected class name, and thus it tries to create a new object. The serializer is unaware that a proxy exists.

@Ocramius
Copy link
Contributor

The serializer is unaware that a proxy exists.

It should stay like that. Is there a test case for this?

Basically:

$this->assertSame(
    get_class($proxy),
    get_class($serializer->unserialize($serializer->serialize($proxy)))
);

@dunglas
Copy link
Member
dunglas commented Aug 27, 2015

Probably that simply changing https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php#L250 by $context['object_to_populate'] instanceof $class will fix the problem.

@bwegrzyn can you provide a snippet we can use to reproduce the problem (ideally a failing unit test)?

@bwegrzyn
Copy link
Author

I do not have a failing test ready, but the following will fail provided that $entity is wrapped in a proxy object, which I believe happens if you do not eagerly load all relations on it.

$manager = $this->get('doctrine')->getManager();
$serializer = $this->get('serializer');

// `User` entity is loaded from repository without eagerly loading relations
$repository = $manager->getRepository('MyBundle:User');

// $entity is instance of Proxies\__CG__\MyBundle\User
$entity = $repository->find(1);

// Deserialize json in to existing $entity
$deserializedEntity = $serializer->deserialize(
    '{"name":"bwegrzyn"}',
    'MyBundle\\Entity\\User',
    'json',
    ['object_to_populate' => $entity]
);

// Observe that $deserializedEntity !== $entity and is missing properties that were loaded from the database

dunglas added a commit that referenced this issue Jan 13, 2016
…unglas)

This PR was merged into the 2.7 branch.

Discussion
----------

[Serializer] Allow to use proxies in object_to_populate

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #15627, api-platform/core#381
| License       | MIT
| Doc PR        | n/a

Allows to populate a proxy (or any class having the given type).

Commits
-------

b16b5b9 [Serializer] Allow to use proxies in object_to_populate
@dunglas dunglas closed this as completed Feb 21, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants
0