I have an API Platform DataTransformer that uses an injected ValidatorInterface to verify the JSON POST body of an incoming request. It is basically a copy of the example in the docs:
<?php
namespace App\DataTransformer;
use ApiPlatform\Core\Validator\ValidatorInterface;
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use App\Entity\Ticket;
use App\DTO\TicketInput;
class InputDataTransformer implements DataTransformerInterface
{
public function __construct(
private readonly ValidatorInterface $validator,
) {}
/**
* @param TicketInput $object
*/
public function transform($object, string $to, array $context = []): Ticket
{
// Validation
$this->validator->validate($object);
$ticket = new Ticket($object->id, $object->content);
return $ticket;
}
public function supportsTransformation($data, string $to, array $context = []): bool
{
return (Ticket::class === $to) && (($context['input']['class'] ?? null) === TicketInput::class);
}
}
Via a ContextBuilder and the supportsTransformation() I ensure that the incoming $object is of type TicketInput. This class is as easy as that:
<?php
namespace App\DTO;
class TicketInput
{
public int $id;
public ?string $content;
}
What I am wondering about: The validator validates via the TicketInput::class that $id must be an integer and is not nullable. $content must be a string and is allowed to be null.
So these are valid inputs:
{
"id": 123,
"content": null
},
{
"id": 123,
"content": "testtest"
}
And this is invalid and gets rejected (400 - Bad request):
{
"id": null,
"content": "foobar"
}
So far so good. But one of my unit tests uses this JSON as request body:
{
"content": "foobar"
}
This broke my code as $id is a required value for the resulting Ticket::class.
I am pretty surprised that the last case was able to pass the validator, as I explicitly define the $id as (not nullable) class variable in TicketInput::class. One could argue that the validator is not obliged to validate properties that are uninitialized at time of validation (which is the case here, according to a var_dump) but nevertheless I am interested in how to define that this property is mandatory and has to be part of the JSON POST body.
The best what I could come up with was the Symfony Validation Constraint #[Assert\NotBlank(allowNull: false)] (see docs), which checks against is_null() and indirectly isset(), but I can't believe that this is the solution and I have to flag every single property like that (the actual TicketInput::class is way more huge and complex than this MWE).
Isn't there any validator config flag that tells it to assure the properties in a DTO are mandatory in a JSON body?
Every simple JSON Schema validator is capable of this and I am wondering why I don't find any hints for this either in the Symfony Docs nor the API Platform docs.