Why do I receive "This value should be of type string" when using a DateTime constraint on Symfony 5?
Asked Answered
H

1

31

I have the following entity (only attached the relevant parts):

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ApiResource(mercure=true)
 * @ORM\Entity(repositoryClass="App\Repository\EventRepository")
 */
class Event {
    /**
     * @ORM\Column(type="datetime")
     * @Assert\DateTime
     * @Assert\NotNull
     */
    private $createdAt;

    public function __construct() {
        $this->createdAt = new \DateTime();
    }

    public function getCreatedAt(): ?\DateTimeInterface {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeInterface $createdAt): self {
        $this->createdAt = $createdAt;
        return $this;
    }
}

Its repository:

class EventRepository extends ServiceEntityRepository {
    public function __construct(ManagerRegistry $registry) {
        parent::__construct($registry, Event::class);
    }
}

When creating a POST request to the event endpoint (via Postman or the Swagger UI), it fails with the following exception:

profiler

Heerlen answered 14/2, 2020 at 13:52 Comment(7)
@delboy1978uk I am using Api Platform, it is doing the insertion automatically.Heerlen
{ "name": "test", "@creator": "/people/23", "description": "desc" } though that's quite irrelevant, as the error is at the createdAt property which is initialized in the constructorHeerlen
I believe the line throwing the error is here. I wonder if you were to cut out ApiPlatform and create an Event yourself and validate it manually, if you would have the issue (it would at least remove complexity). Maybe it's the way ApiPlatform loads the object?Woodbury
Hmm, I have another entity (that is not connected to API Platform) that contains a DateTime with the same constraints and that works as expected.Heerlen
We're using AP 2.1 and I recall there being something about how it generates objects maybe (it's been a little while)? You can go into the validator file in vendor and dump() what it's getting to check, this is where I would start.Woodbury
Looking at the line, I see it's checking whether the value is a) scalar or b) an object with a __toString method defined. However, DateTime does not have a __toString method so I think that is the problem. Though, why in my non-API Platform entity it works as expected?Heerlen
I am using Symfony 5.0.4Heerlen
S
87

You are using the wrong of assertion.

Date expects a string or an object that can be cast into a string. And a DateTimeInterface is neither.

You should be using a Type constraint.

/**
 * @Assert\Type("\DateTimeInterface")
 */
 private $createdAt;

The ability to use Assert\Date to validate DateTime objects was deprecated on Symfony 4.2, and on Symfony 5.0 it was removed altogether.

Siouan answered 14/2, 2020 at 15:20 Comment(6)
Ah! We have @Assert\Type("datetime") in some places, so that makes sense.Woodbury
This was totally new to me! I was ever since using @Assert\DateTime(groups={"new"}) and now got that weird validator message on my new symfony 5.1 project. Now I changed to the example given above and it works. Awesome! Thank youTeacup
If you need to customize the assertion a bit, here you are an example: @Assert\Type(type="\DateTimeInterface", message="Custom Message")Diamagnet
What about the date format ? This option exists for #[Assert\DateTime(format: 'Y-m-d\TH:i:sP')]Rubie
What about it, @Delphine? You can use that option for Assert\DateTime because you validating a string. It does not make sense for Assert\Type, since this assertion checks the property is of a specific type, so the format is actually irrelevant (a DateTime object does not have "a format").Siouan
Thanks for your reply. I doubled my Assert with #[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\TH:i:sP'])] just to be sure (API project)Rubie

© 2022 - 2024 — McMap. All rights reserved.