I have two entities that reference each other and have not null constraints, and am trying to persist them with the following script:
$parentEntity = new \App\Entity\ParentEntity();
$childEntity = new \App\Entity\ChildEntity();
$childEntity->setParentEntity($parentEntity);
$parentEntity->setRootChildEntity($childEntity);
$this->entityManager->persist($parentEntity);
$this->entityManager->persist($childEntity);
printf('$parentEntity->getId(): %s'.PHP_EOL, $parentEntity->getId());
printf('$childEntity->getId(): %s'.PHP_EOL, $childEntity->getId());
printf('$parentEntity->getRootChildEntity()->getId(): %s'.PHP_EOL, $parentEntity->getRootChildEntity()->getId());
printf('$childEntity->getParentEntity()->getId(): %s'.PHP_EOL, $childEntity->getParentEntity()->getId());
$this->entityManager->flush();
When executing the script, both entities are populated with integer IDs after being persisted (obtained by the SELECT NEXTVAL queries), but when flushed, instead NULL is used and I get a not null violation.
If I was just getting a foreign key constraint, I would understand why and I could get around it by making the constraints deferrable or bypassing them by temporarily disabling triggers.
But not so with a null violation. Why is this happening? Without removing the not null constraint which I wish to keep, is there a way around it?
doctrine.DEBUG: SELECT NEXTVAL('parent_entity_id_seq') [] []
doctrine.DEBUG: SELECT NEXTVAL('child_entity_id_seq') [] []
$parentEntity->getId(): 1
$childEntity->getId(): 1
$parentEntity->getRootChildEntity()->getId(): 1
$childEntity->getParentEntity()->getId(): 1
doctrine.DEBUG: "START TRANSACTION" [] []
doctrine.DEBUG: INSERT INTO parent_entity (id, root_child_entity_id) VALUES (?, ?) {"1":1,"2":null} []
doctrine.DEBUG: "ROLLBACK" [] []
console.CRITICAL: Error thrown while running command "app:add-tenant". Message: "An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column "root_child_entity_id" of relation "parent_entity" violates not-null constraint DETAIL: Failing row contains (1, null)." {"exception":"[object] (Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException(code: 7): An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"root_child_entity_id\" of relation \"parent_entity\" violates not-null constraint\nDETAIL: Failing row contains (1, null). at /srv/api/vendor/doctrine/dbal/src/Driver/API/PostgreSQL/ExceptionConverter.php:51)\n[previous exception] [object] (Doctrine\\DBAL\\Driver\\PDO\\Exception(code: 7): SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"root_child_entity_id\" of relation \"parent_entity\" violates not-null constraint\nDETAIL: Failing row contains (1, null). at /srv/api/vendor/doctrine/dbal/src/Driver/PDO/Exception.php:26)\n[previous exception] [object] (PDOException(code: 23502): SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"root_child_entity_id\" of relation \"parent_entity\" violates not-null constraint\nDETAIL: Failing row contains (1, null). at /srv/api/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:101)","command":"app:add-tenant","message":"An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"root_child_entity_id\" of relation \"parent_entity\" violates not-null constraint\nDETAIL: Failing row contains (1, null)."} []
My entities:
<?php
namespace App\Entity;
use App\Repository\ParentEntityRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class ParentEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToOne(targetEntity: ChildEntity::class)]
#[ORM\JoinColumn(nullable: false)]
private $rootChildEntity;
public function getId(): ?int
{
return $this->id;
}
public function getRootChildEntity(): ?ChildEntity
{
return $this->rootChildEntity;
}
public function setRootChildEntity(?ChildEntity $rootChildEntity): self
{
$this->rootChildEntity = $rootChildEntity;
return $this;
}
}
<?php
namespace App\Entity;
use App\Repository\ChildEntityRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class ChildEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\ManyToOne(targetEntity: ParentEntity::class)]
#[ORM\JoinColumn(nullable: false)]
private $parentEntity;
public function getId(): ?int
{
return $this->id;
}
public function getParentEntity(): ?ParentEntity
{
return $this->parentEntity;
}
public function setParentEntity(?ParentEntity $parentEntity): self
{
$this->parentEntity = $parentEntity;
return $this;
}
}
which generates the following SQL:
doctrine.DEBUG: CREATE SEQUENCE child_entity_id_seq INCREMENT BY 1 MINVALUE 1 START 1 [] []
doctrine.DEBUG: CREATE SEQUENCE parent_entity_id_seq INCREMENT BY 1 MINVALUE 1 START 1 [] []
doctrine.DEBUG: CREATE TABLE child_entity (id INT NOT NULL, parent_entity_id INT NOT NULL, PRIMARY KEY(id)) [] []
doctrine.DEBUG: CREATE INDEX IDX_677D8034706E52B3 ON child_entity (parent_entity_id) [] []
doctrine.DEBUG: CREATE TABLE parent_entity (id INT NOT NULL, root_child_entity_id INT NOT NULL, PRIMARY KEY(id)) [] []
doctrine.DEBUG: CREATE INDEX IDX_413B87AEE5B68E27 ON parent_entity (root_child_entity_id) [] []
doctrine.DEBUG: ALTER TABLE child_entity ADD CONSTRAINT FK_677D8034706E52B3 FOREIGN KEY (parent_entity_id) REFERENCES parent_entity (id) NOT DEFERRABLE INITIALLY IMMEDIATE [] []
doctrine.DEBUG: ALTER TABLE parent_entity ADD CONSTRAINT FK_413B87AEE5B68E27 FOREIGN KEY (root_child_entity_id) REFERENCES child_entity (id) NOT DEFERRABLE INITIALLY IMMEDIATE [] []