28

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

yivi
  • 42,438
  • 18
  • 116
  • 138
Toma
  • 2,764
  • 4
  • 25
  • 44
  • @delboy1978uk I am using Api Platform, it is doing the insertion automatically. – Toma Feb 14 '20 at 13:59
  • ```{ "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 constructor – Toma Feb 14 '20 at 14:40
  • I believe the [line throwing the error is here](https://github.com/symfony/symfony/blob/05f71d3fd575062be3e5cbd7000a2666f4e1a247/src/Symfony/Component/Validator/Constraints/DateTimeValidator.php#L37). 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? – Jared Farrish Feb 14 '20 at 14:43
  • 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. – Toma Feb 14 '20 at 14:50
  • 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. – Jared Farrish Feb 14 '20 at 15:00
  • 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? – Toma Feb 14 '20 at 15:15
  • I am using Symfony 5.0.4 – Toma Feb 14 '20 at 16:04

1 Answers1

79

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.

yivi
  • 42,438
  • 18
  • 116
  • 138
  • 3
    Ah! We have `@Assert\Type("datetime")` in some places, so that makes sense. – Jared Farrish Feb 14 '20 at 15:22
  • 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 you – Chris P. Bacon Nov 16 '20 at 20:02
  • 1
    If you need to customize the assertion a bit, here you are an example: @Assert\Type(type="\DateTimeInterface", message="Custom Message") – Samuel Vicent Dec 14 '20 at 14:43