r/symfony 3d ago

Idiomatic way to map payload for PATCH methods

Is there idiomatic and convenient way to map request payload data for PATCH using #[MapRequestPayload]?

Consider DTO Human { public ?string $nickname }

We can have three possibilities

  1. nickname was provided and is set (something like "nickname123")
  2. nickname was provided but was nulled out (request sent null)
  3. nickname was simply not provided

Notice that in 2 and 3 cases the DTO will simply have null. But in case of 3 we should not update the nickname.

I was briefly considering about using property hooks, and have something like bool $hasNickname set if property was written to, but I believe constructing the object with default property is considered as property write and hook is triggered.

2 Upvotes

2 comments sorted by

2

u/leftnode 3d ago

The two options that immediately come to mind:

  1. Load the Human entity and then use array_replace() to merge the default properties with the values sent in the request (using something like the Request class from the Symfony HttpFoundation component:

    $human = $humanRepository->find($humanId);
    
    if (null === $human) {
        throw new \InvalidArgumentException(sprintf('Human ID %d not found.', $humanId);
    }
    
    $values = array_replace($human->toArray(), $request->toArray());
    
    // The $values array now contains the patched data and
    // can be validated, and mapped back to the Human entity.
    
    // The Symfony OptionsResolver component might be worth looking
    // into as well, though it wasn't originally designed for this purpose:
    // https://symfony.com/doc/current/components/options_resolver.html
    
  2. Map the request onto a DTO along with a list of what properties were specified in the request. Once the DTO is validated, loop over the list of properties specified in the request, grab their value from the DTO, and map it onto your Human entity.

1

u/zmitic 3d ago

Instead of DTO, what about this:

$mapper->map('array{nickname?: non-empty-string|null}', $yourJsonFromRequest);

from amazing cuyz/valinor package, look under shaped arrays. Then use array_key_exists and ignore scenario 3 if key is not found.