'Multiple Fixture Classes results in Persist errors like Duplicate Entry and Entity not persisted Symfony 5
Versions: PHP 8.1
Worked in Symfony 5.3 finding behavior in Symfony 5.4.
"doctrine/doctrine-fixtures-bundle": "^3.4"
"doctrine/doctrine-bundle": "^2.2"
"doctrine/orm": "^2.8"
General Problem: Multiple Fixture classes causes errors with references from other fixture classes
Expectations from old 5.3 Project: On the old project I am able to make tons of separate Fixtures classes and run them all with DependentFixturesInterface and use the references already created (persisted) to then create the relations needed for the other fixtures. Example: I create Users first and then Teams, for each Team these is a ManyToOne $createdUser column that relates to a User that creates it. But I can make a UserFixtures class and safe the references (as seen in symfony casts etc.) then runs the TeamFixtures class to use the references in UserFixtures for TeamFixtures (all standard understanding)
Behavior in new 5.4 project: In the new project I am no way able to create multiple fixture classes. In the same exact example above when I try to create the same relationship I get the follow error
A new entity was found through the relationship 'App\Entity\Team#createdUser' that was not configured to cascade persist operations for entity: App\Entity\User@whateveruser. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'App\Entity\User#__toString()' to get a clue.
So then I listen to the exception and add cascade={"persist"} to the entity relation, run the fixtures again and I get the following error:
Duplicate entry *** for key ****
To me this means somehow it is not correctly persisting the Users in the first place or I am completely off as to how Doctrine works.
This is the loader in my main fixture class
public function loadData(): void
{
$this->generateMockIntervals();
// Users, Contacts
$this->setGroup(self::TEST, 3)->createMany('generateFakeUser', [$this->testPasswordHash, self::TEST_EMAIL_ITERATED]);
$this->setGroup(self::DUMMY, 500)->createMany('generateFakeUser', [$this->dummyPasswordHashed]);
$this->setGroup(self::teamGroupName(), 100)->createMany('generateTeams');
$configArray = array(
MemberInterface::MEMBERS => array(
self::GEN => [$this, 'generateUserMembers'],
self::RANGE => 20,
self::R => self::REF_TYPES[0],
),
);
if (!empty($configArray)) {
$this->groupForAll($configArray);
} else {
$this->pr("Class %s not configured yet", static::class);
}
}
The createMany function loops through each item and creates the new User references and saves it in the reference Repo (straight from SymfonyCasts). groupAll does the same thing but loops through the references that is configured for each new reference key. I super enhanced symfony casts createMany function. If you have not seen it this is the function that EVERY entity is passed to.
protected function manageReference($entity, $groupName)
{
if (null === $entity) {
throw new \LogicException('Did you forget to return the entity object from your callback to BaseFixture::createMany()?');
}
$this->manager->persist($entity);
// store for usage later as App\Entity\ClassName_#COUNT#
$storage = sprintf('%s_%d', $groupName, $this->i);
$this->addReference($storage, $entity);
}
ONLY DIFFERENCE between 5.3 project and 5.4 project: The only major difference I can see that is causing this problem is that in my old (5.3) project I had ALL Bidirectional variables built into the entity. In 5.4 I removed ALL/MOST of the Bidirectional relationships for my entities in theory to generate less queries.
Basically if I were to take out this part here
$this->setGroup(self::teamGroupName(), 100)->createMany('generateTeams');
$configArray = array(
MemberInterface::MEMBERS => array(
self::GEN => [$this, 'generateUserMembers'],
self::RANGE => 20,
self::R => self::REF_TYPES[0],
),
);
if (!empty($configArray)) {
$this->groupForAll($configArray);
} else {
$this->pr("Class %s not configured yet", static::class);
}
And put it into a new fixture I start getting the Duplicate Entry and "must persist" errors. But as you can see I am Persisting every single entity and I am flushing every 25 entities are Peristed
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
