I want to parse JSON Requests into nested DTOs with MapRequestPayload. The goal is to have the data into the DTOs validated.
Example JSON:
{
"meta": {
"locale": "ger",
"validateOnly": false
},
"content": {
"firstName": "firstName",
"lastName": "lastName",
"displayMode": "system",
"favoriteMandates": [
{
"mandateId": "{{mandateId}}",
"position": 0
}
]
}
}
“meta” always contains the same set of data, while content varies depending on endpoint.
My first approach was something like this:
use SymfonyComponentValidatorConstraints as Assert;
final class UpdateUserRequestDTO
{
public function __construct(
#[AssertValid] public Meta $meta,
#[AssertValid] public UpdateUserDTO $content,
)
{}
}
use SymfonyComponentValidatorConstraints as Assert;
use CustomConstrains;
final readonly class UpdateUserSettingsDto
{
public Name $name;
public FavoriteMandatesCollection $favoriteMandates;
public function __construct(
string $firstName,
string $lastName,
#[AssertIsArray] array $favoriteMandates,
#[CustomConstrainsIsEnumValue(DisplayModeEnum::class)] string $displayMode,
) {
$this->name = Name::fromStrings($firstName, $lastName);
$this->favoriteMandates = FavoriteMandatesCollection::fromInputArray($favoriteMandates);
$this->displayMode = DisplayModeEnum::fromString($displayMode);
}
}
The problem is that this would mean 2 Classes per endpoint where the difference between the requestDTO classes is just the type for $content. While this most likely would work it feels kinda dirty, especially if the project grows bigger.
My second approach was then defining $content as array (which would only 1 reusable requestDTO) and then calling the constructor with $dto = new UpdateUserSettingsDto(...$content) at this approach I seem to have missed something, since the constrains aren’t checked.