Keeping Doctrine Entities Honest with DTOs and ObjectMapper
Originally posted on https://symfonycasts.com/blog/honest-entities Your Doctrine entities are lying to you! For years, the standard way to build Doctrine entities in Symfony has looked something like this (and it's still what MakerBundle generates today): #[ORM\Entity] class ConferenceTalk { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null ; #[Assert\NotBlank] #[ORM\Column(length: 255)] private ?string $title = null ; #[ORM\Column(type: Types::TEXT, nullable: true)] private ?string $abstract = null ; public function getId (): ?int { return $this -> id ; } public function getTitle (): ?string { return $this -> title ; } public function setTitle ( ?string $title ): static { $this -> title = $title ; return $this ; } public function getAbstract (): ?string { return $this -> abstract ; } public function setAbstract ( ?string $abstract ): static { $this -> abstract = $abstract ; return $this ; } } At first glance, this looks perfectly reasonable. The title field is required. We know that because it has a NotBlank constraint and the database column is not nullable. But look closer. private ?string $title = null ; public function setTitle ( ?string $title ): static public function getTitle (): ?string According to the PHP type system, the title is optional. In fact, the public API of this class explicitly allows us to set it to null . That means this is perfectly valid: $talk = new ConferenceTalk (); And so is this: $talk = new ConferenceTalk (); $talk -> setTitle ( null ); Both objects represent a conference talk that can never be successfully persisted. Eventually, Doctrine catches the problem: $talk = new ConferenceTalk (); $entityManager -> persist ( $talk ); $entityManager -> flush (); // boom! SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null The database knows that a conference talk must have a title. Our PHP code does not. So why do we build entities this way? Historically, this pattern was optimized for simpli