Упорствовать объект с двумя иностранными идентичностями в доктрине

Я создал сущность, используя синтаксис yml в моем пакете Symfony, в папке Resources / config / doctrine:

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

Эта часть работает правильно. Но когда я создаю некоторые объекты, как в следующем коде, я получаю сообщение об ошибке, что мне нужно использовать метод flush, чтобы получить идентификаторы для внешних ключей.

Это фрагмент кода, который я сейчас использую:

    // 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();

Если я не вызываю сброс в цикле foreach, я получаю следующую ошибку, которую я полностью понимаю, но нет ли более элегантного решения этой проблемы?

Doctrine \ ORM \ ORMException: сущность типа Sulu\Bundle\TranslateBundle\Entity\Translation имеет идентичность через внешнюю сущность Sulu\Bundle\TranslateBundle\Entity\Code, однако сама эта сущность не имеет никакой идентичности. Вы должны вызвать EntityManager#persist() для связанной сущности и убедиться, что идентификатор был сгенерирован, прежде чем пытаться сохранить 'Sulu\Bundle\TranslateBundle\Entity\Translation'. В случае генерации идентификатора после вставки (например, MySQL Auto-Increment или PostgreSQL SERIAL) это означает, что вы должны вызвать EntityManager#flush() между обеими операциями сохранения.

2 ответа

Решение

К сожалению, согласно Doctrine Docs вы должны вызвать flush для получения идентификаторов ваших внешних ключей:

Сгенерированные идентификаторы сущностей / первичные ключи гарантированно будут доступны после следующей успешной операции сброса, в которой участвует рассматриваемая сущность. Вы не можете полагаться на то, что сгенерированный идентификатор будет доступен сразу после вызова persist. Обратное тоже верно. Вы не можете полагаться на то, что сгенерированный идентификатор недоступен после неудачной операции очистки.

Вы пробовали что-то вроде следующего?

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

В вашем классе Перевод

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;
}

Тогда ваш код может быть примерно таким:

$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();

Примечание: cascade: ["persist"] - каскады сохраняют операции для связанных объектов. Ссылка на сайт

Другие вопросы по тегам