I am building a project management tool for my team in Symfony 3. I am using ramsey/uuid-doctrine for the IDs in the system.
So far, this hasn't been a problem with One-to-Many or Many-to-One associations, but when I try to persist a One-to-One association, Doctrine is not converting the associated entity to its UUID, and instead leaving a null
value in the SQL.
In this example, I have a WikiPage which can have multiple WikiPageVersions. The WikiPage has a One-to-Many association with WikiPageVersion (the versions
property: for all the versions of the page), but also a One-to-One (Unidirectional) association with WikiPageVersion (the currentVersion
property: for the, well, current version).
The WikiPage also has a Many-to-One associations with Project (to track which project the wiki page is for) and that property is populated correctly.
The WikiPage Entity
/**
* @ORM\Table(name="wiki_page")
* @ORM\Entity(repositoryClass="AppBundle\Repository\Project\WikiPageRepository")
*/
class WikiPage
{
/**
* @var Uuid
* @ORM\Id
* @ORM\Column(type="uuid")
*/
protected $id;
/**
* @var Project
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Project\Project", inversedBy="wikiPages")
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
*/
protected $project;
/**
* @var string
* @ORM\Column(name="title", type="text")
* @Assert\NotBlank()
*/
protected $title;
/**
* @var HiveWikiPageVersion
* @ORM\OneToOne(targetEntity="AppBundle\Entity\Project\WikiPageVersion", fetch="EAGER")
*/
protected $currentVersion;
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="AppBundle\Entity\Project\WikiPageVersion", mappedBy="wikiPage", cascade={"persist", "remove"})
*/
protected $versions;
// -- Class Methods
}
The WikiPageVersion Entity
/**
* @ORM\Table(name="wiki_page_version")
* @ORM\Entity(repositoryClass="AppBundle\Repository\Project\WikiPageVersionRepository")
*/
class WikiPageVersion
{
/**
* @var Uuid
* @ORM\Id
* @ORM\Column(type="uuid")
*/
protected $id;
/**
* @var WikiPage
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Project\WikiPage", inversedBy="versions")
* @ORM\JoinColumn(name="wiki_page_id", referencedColumnName="id")
* @Assert\NotBlank()
*/
protected $wikiPage;
/**
* @var string
* @ORM\Column(name="content", type="text")
* @Assert\NotBlank()
*/
protected $content;
/**
* @var string
* @ORM\Column(name="version_comment", type="string", length=255)
* @Assert\NotNull()
*/
protected $versionComment;
/**
* @var HiveWikiPageVersion
* @ORM\OneToOne(targetEntity="AppBundle\Entity\Project\WikiPageVersion")
* @ORM\JoinColumn(name="previous_version", referencedColumnName="id")
* @Assert\Type(type="Odev\Hive\Model\Entity\Project\WikiPageVersion")
*/
protected $previousVersion;
/**
* @var \DateTimeInterface
* @ORM\Column(name="created", type="datetime")
* @Assert\NotBlank()
*/
protected $created;
/**
* @var User
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* @ORM\JoinColumn(name="created_by", referencedColumnName="id")
* @Assert\NotBlank()
*/
protected $createdBy;
}
// -- Methods
Here is the error I get from Doctrine when I try to perist the WikiPage:
[Doctrine\DBAL\Exception\NotNullConstraintViolationException]
An exception occurred while executing 'INSERT INTO wiki_page (id, project_home, title, project_id, current_version_id) VALUES (?, ?, ?, ?, ?)' with params ["ddc1f51a-f5d9-489f-89bb-cd79f3393af0", 1
, "Technical Reviews Wiki", "5138b185-b10b-48ac-a102-bdea1139c911", null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'current_version_id' cannot be null
and the exception trace (this exception is from saving during fixtures loading):
Exception trace:
() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractMySQLDriver.php:112
Doctrine\DBAL\Driver\AbstractMySQLDriver->convertException() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:128
Doctrine\DBAL\DBALException::driverExceptionDuringQuery() at /home/vagrant/hive/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:177
Doctrine\DBAL\Statement->execute() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281
Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1014
Doctrine\ORM\UnitOfWork->executeInserts() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378
Doctrine\ORM\UnitOfWork->commit() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356
Doctrine\ORM\EntityManager->flush() at /home/vagrant/hive/src/Odev/Hive/Infrastructure/AppBundle/DataFixtures/ORM/LoadProjectData.php:89
Odev\Hive\Infrastructure\AppBundle\DataFixtures\ORM\LoadProjectData->load() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/AbstractExecutor.php:121
Doctrine\Common\DataFixtures\Executor\AbstractExecutor->load() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php:88
Doctrine\Common\DataFixtures\Executor\ORMExecutor->Doctrine\Common\DataFixtures\Executor\{closure}() at n/a:n/a
call_user_func() at /home/vagrant/hive/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:233
Doctrine\ORM\EntityManager->transactional() at /dev/shm/app/cache/dev/appDevDebugProjectContainer.php:5645
DoctrineORMEntityManager_00000000015ce1f5000000002a2c79364ae5d79093a662a969d1540330e84087->transactional() at /home/vagrant/hive/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php:90
Doctrine\Common\DataFixtures\Executor\ORMExecutor->execute() at /home/vagrant/hive/vendor/doctrine/doctrine-fixtures-bundle/Command/LoadDataFixturesDoctrineCommand.php:118
Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand->execute() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:262
Symfony\Component\Console\Command\Command->run() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:848
Symfony\Component\Console\Application->doRunCommand() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:189
Symfony\Component\Console\Application->doRun() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:80
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at /home/vagrant/hive/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:120
Symfony\Component\Console\Application->run() at /home/vagrant/hive/bin/console:29
At this point I am lost - if there is any suggestions of where I should be looking or if anyone else has run into this problem, I would love and guidance that could be provided.
Update
As requested by matteo:
Here is the load
method of the fixture that creates the records and throws the error.
/**
* {@inheritDoc}
* @param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
// Create ODEV Project Section
$section = new ProjectSection(
Uuid::uuid4(),
'ODEV',
'ODEV specific projects'
);
$manager->persist($section);
$this->addReference('section-odev', $section);
// Create Technical Review Project -> public
$project = new Project(
Uuid::uuid4(),
'techreview',
$section,
'Technical Reviews',
'Technical Reviews for Work Requests',
false,
false,
$this->getReference('user-system'),
$this->getReference('user-system'),
'',
$this->getReference('user-system')
);
// VarDumper::dump($project->getWikiHome()->getId());
// VarDumper::dump($project->getCreatedBy()->getId());
$manager->persist($project);
$this->addReference('project-tech-review', $project);
$manager->flush();
}
The two VarDumper::dump()
commands were to confirm that the associations were getting created.
The actual WikiPage gets generated in the Project's constructor and the WikiPageVersion is generated in the WikiPages's constructor.
Here is Project
's constructor:
public function __construct(
string $id,
string $identifier,
ProjectSection $section,
string $name,
string $description,
bool $private,
bool $sensitive,
User $owner,
User $contact,
string $homepage,
User $createdBy
) {
$this->id = Uuid::fromString($id);
$this->identifier = $identifier;
$this->projectSection = $section;
$this->name = $name;
$this->description = $description;
$this->private = $private;
$this->sensitive = $sensitive;
$this->owner = $owner;
$this->contact = $contact;
$this->homepage = $homepage;
$this->createdBy = $createdBy;
$this->created = new \DateTimeImmutable();
$this->updatedBy = $createdBy;
$this->updated = new \DateTimeImmutable();
$this->archived = false;
$this->workRequest = null;
// set up collections
$this->teamMembers = new ArrayCollection();
$this->issues = new ArrayCollection();
$this->folders = new ArrayCollection($this->defaultFolders());
$this->wikiHome = $this->defaultWikiPage();
$this->wikiPages = new ArrayCollection([$this->wikiHome]);
$this->labels = new ArrayCollection($this->defaultLabels());
$this->milestones = new ArrayCollection($this->defaultMilestones());
}
protected function defaultWikiPage(): WikiPage
{
return new WikiPage(Uuid::uuid4(), $this, $this->name.' Wiki', '', $this->createdBy);
}
And the constructor of WikiPage
:
public function __construct(string $id, Project $project, string $title, string $content, User $createdBy)
{
$this->id = Uuid::fromString($id);
$this->project = $project;
$this->title = $title;
$this->content = $content;
$this->created = new \DateTimeImmutable();
$this->createdBy = $createdBy;
$this->currentVersion = $this->createFirstVersion($content, $createdBy);
$this->versions = new ArrayCollection([$this->currentVersion]);
}
protected function createFirstVersion(string $content, User $createdBy)
{
return new WikiPageVersion(Uuid::uuid4(), $this, $content, $createdBy, 'Page Created');
}
Hope that helps.
When WikiPage Entity tries to INSERT it is trying to insert all its properties. Do a check for version and if === null unset the key index. then when the INSERT fires the parm array is only 4 keys.