Reputation: 2056
I have created an entity using the yml-syntax in my symfony bundle, in the Resources/config/doctrine folder:
Sulu\Bundle\TranslateBundle\Entity\Translation:
type: entity
table: tr_translations
id:
code:
type: string
column: idCodes
associationKey: id
catalogue:
type: string
column: idCatalogues
associationKey: id
fields:
value:
type: text
manyToOne:
code:
targetEntity: Code
inversedBy: tr_codes
joinColumn:
name: idCodes
referencedColumnName: id
catalogue:
targetEntity: Catalogue
inversedBy: tr_catalogues
joinColumn:
name: idCatalogues
referencedColumnName: id
This part is working correctly. But when I create some objects like in the following code, I get an error message that I have to use the flush method, in order to get IDs for the foreign keys.
This is the code snippet I am currently using:
// create a new package and catalogue for the import
$package = new Package();
$package->setName($this->getName());
$catalogue = new Catalogue();
$catalogue->setLocale($this->getLocale());
$catalogue->setPackage($package);
$this->em->persist($package);
$this->em->persist($catalogue);
// load the file, and create a new code/translation combination for every message
$fileCatalogue = $loader->load($this->getFile(), $this->getLocale());
foreach ($fileCatalogue->all()['messages'] as $key => $message) {
$code = new Code();
$code->setPackage($package);
$code->setCode($key);
$code->setBackend(true);
$code->setFrontend(true);
$translate = new Translation();
$translate->setCode($code);
$translate->setValue($message);
$translate->setCatalogue($catalogue);
$this->em->persist($code);
$this->em->flush(); //FIXME no flush in between, if possible
$this->em->persist($translate);
}
// save all the changes to the database
$this->em->flush();
If I don't call the flush in the foreach loop I get the following error, which I totally understand, but isn't there a more elegant solution for this problem?
Doctrine\ORM\ORMException : Entity of type Sulu\Bundle\TranslateBundle\Entity\Translation has identity through a foreign entity Sulu\Bundle\TranslateBundle\Entity\Code, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'Sulu\Bundle\TranslateBundle\Entity\Translation'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.
Upvotes: 3
Views: 3489
Reputation: 965
Unfortunately, as per Doctrine Docs you have to call flush to get IDs for your foreign keys:
Generated entity identifiers / primary keys are guaranteed to be available after the next successful flush operation that involves the entity in question. You can not rely on a generated identifier to be available directly after invoking persist. The inverse is also true. You can not rely on a generated identifier being not available after a failed flush operation.
Upvotes: 4
Reputation: 93
Have you tried something like the following?
YAML:
Sulu\Bundle\TranslateBundle\Entity\Translation:
type: entity
table: tr_translations
id:
code:
type: string
column: idCodes
associationKey: id
catalogue:
type: string
column: idCatalogues
associationKey: id
fields:
value:
type: text
manyToOne:
code:
targetEntity: Code
cascade: ["persist"]
inversedBy: tr_codes
joinColumn:
name: idCodes
referencedColumnName: id
catalogue:
targetEntity: Catalogue
cascade: ["persist"]
inversedBy: tr_catalogues
joinColumn:
name: idCatalogues
referencedColumnName: id
In your class Translation
public function __construct()
{
$this->code = new \Doctrine\Common\Collections\ArrayCollection();
$this->catalogue = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addCatalogue($catalogue)
{
$this->catalogue[] = $catalogue;
return $this;
}
public function addCode($code)
{
$this->code[] = $code;
return $this;
}
Then your code can be something like this:
$package = new Package();
$package->setName($this->getName());
$catalogue = new Catalogue();
$catalogue->setLocale($this->getLocale())->setPackage($package);
$fileCatalogue = $loader->load($this->getFile(), $this->getLocale());
foreach ($fileCatalogue->all()['messages'] as $key => $message) {
$code = new Code();
$code->setPackage($package)
->setCode($key)
->setBackend(true)
->setFrontend(true);
$translate = new Translation();
$translate->addCode($code)
->setValue($message)
->addCatalogue($catalogue);
$this->em->persist($translate);
}
$this->em->flush();
Note: cascade: ["persist"] - Cascades persist operations to the associated entities. Link
Upvotes: 0