API Platform DTO Validator - Missing properties are no violation

626 views Asked by At

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.

0

There are 0 answers