Skip to content

[Question/Potential bug] UniqueEntity validation reloads deserialized entity and discards user data #3164

@tezvi

Description

@tezvi

Hi api-platform fans!

This seems like a design issue, not necessarily with api-platform.
Here's the scenario:

  • I have an entity A
  • There's a UniqueEntity constraint validation defined on ManyToOne property B with EAGER fetch mode
  • When I issue a PUT request to change that property with either a null or proper value the WriteListener persists data with wrong value / state

Behind the scenes

  1. ReadListener loads entity and sets it in request.attributes.data

    $request->attributes->set('data', $data);

  2. DeserializeListener deserializes JSON payload and replaces request.attributes.data item

    $request->attributes->set(
    'data',
    $this->serializer->deserialize($request->getContent(), $context['resource_class'], $format, $context)
    );

  3. Symfony injects request.attributes.data as $data argument in a custom controller

  4. ValidateListener triggers validator on controller result from previous step and invokes UniqueEntityValidator which seems to reload the entity and there fore effectively discards deserialized data from DeserializeListener step https://github.com/symfony/doctrine-bridge/blob/ca6f8537ed349250940a58266630875faec42303/Validator/Constraints/UniqueEntityValidator.php#L138

  5. WriteListener retrieves controller result that references entity that has been reloaded and persists wrong data

    $persistResult = $this->dataPersister->persist($controllerResult, $attributes);

Result

You are unable to set entity association to null if it has been previously defined and persisted in database.

There's another gotcha, this scenario occurs only when property B in entity A defines EAGER fetch mode. For LAZY and EXTRA_LAZY Doctrine will not reload and rewrite property value.

Simple solution is to not use UniqueEntity validation or EAGER fetch mode. However, I was wondering if there is a good soul to give me an alternative approach. Is there a way to somehow prevent UniqueEntityValidator and Doctrine to fully reload entity state per API request operation?

Tried to pass a cloned entity to validator but that seems to brake down the UniqueEntityValidator

$this->validator->validate($controllerResult, ['groups' => $validationGroups]);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions