web-dev-qa-db-ja.com

「AUTO」戦略を使用する場合、DoctrineでIDを明示的に設定します

私のエンティティは、IDにこの注釈を使用しています。

_/**
 * @orm:Id
 * @orm:Column(type="integer")
 * @orm:GeneratedValue(strategy="AUTO")
 */
protected $id;
_

クリーンなデータベースから、古いデータベースから既存のレコードをインポートし、同じIDを保持しようとしています。次に、新しいレコードを追加するときに、MySQLに通常どおりID列を自動インクリメントさせます。

残念ながら、Doctrine2は指定されたIDを完全に無視するようです。


新しいソリューション

以下の推奨事項に従って、次が推奨されるソリューションです。

_$this->em->persist($entity);

$metadata = $this->em->getClassMetaData(get_class($entity));
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
_

旧ソリューション

Doctrineはジェネレータ戦略を決定するためにClassMetaDataからピボットされるため、EntityManagerでエンティティを管理した後に変更する必要があります。

_$this->em->persist($entity);

$metadata = $this->em->getClassMetaData(get_class($entity));
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

$this->em->flush();
_

これをMySQLでテストしたところ、期待どおりに機能しました。つまり、カスタムIDを持つエンティティはそのIDで保存され、IDが指定されていないエンティティはlastGeneratedId() + 1を使用しました。

92
Eric

ソリューションはMySQLで正常に動作しますが、シーケンスベースであるため、PostgreSQLで動作させることができませんでした。

この行を追加して、完全に機能するようにします。

$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());

宜しくお願いします、

47
nicolasbui

おそらく何がdoctrineが変更されたが、今正しい方法は:

$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
28
Alexey B.

エンティティが クラステーブルの継承 の一部である場合、bothのクラスメタデータのid-generatorを変更する必要がありますエンティティ(永続化するエンティティとルートエンティティ)

7
Weregoat

新しいソリューションは、すべてのエンティティが挿入前にIDを持っている場合にのみ正常に機能します。 1つのエンティティにIDがあり、別のエンティティにはない場合-新しいソリューションは失敗します。

私はすべてのデータをインポートするためにこの関数を使用します:

function createEntity(\Doctrine\ORM\EntityManager $em, $entity, $id = null)
{
    $className = get_class($entity);
    if ($id) {
        $idRef = new \ReflectionProperty($className, "id");
        $idRef->setAccessible(true);
        $idRef->setValue($entity, $id);

        $metadata = $em->getClassMetadata($className);
        /** @var \Doctrine\ORM\Mapping\ClassMetadataInfo $metadata */
        $generator = $metadata->idGenerator;
        $generatorType = $metadata->generatorType;

        $metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
        $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

        $unitOfWork = $em->getUnitOfWork();
        $persistersRef = new \ReflectionProperty($unitOfWork, "persisters");
        $persistersRef->setAccessible(true);
        $persisters = $persistersRef->getValue($unitOfWork);
        unset($persisters[$className]);
        $persistersRef->setValue($unitOfWork, $persisters);

        $em->persist($entity);
        $em->flush();

        $idRef->setAccessible(false);
        $metadata->setIdGenerator($generator);
        $metadata->setIdGeneratorType($generatorType);

        $persisters = $persistersRef->getValue($unitOfWork);
        unset($persisters[$className]);
        $persistersRef->setValue($unitOfWork, $persisters);
        $persistersRef->setAccessible(false);
    } else {
        $em->persist($entity);
        $em->flush();
    }
}

Solution for Doctrine 2.5 and MySQL

「新しいソリューション」は、Doctrine 2.5およびMySQLでは機能しません。使用する必要があります。

$metadata = $this->getEntityManager()->getClassMetaData(Entity::class);
$metadata->setIdGenerator(new AssignedGenerator());
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_‌​NONE);

ただし、他のDBMSをまだ試したことがないため、MySQLについてのみ確認できます。

3
fishbone

a library を作成して、Doctrineエンティティの将来のIDを設定します。すべてのキューIDが消費されると、元のID生成戦略に戻り、影響を最小限に抑えます。このようなコードを繰り返す必要がないように、単体テスト用の簡単なドロップイン。

1
Villermen

Villermen に触発されて、ライブラリ tseho/doctrine-assigned-identity を作成しました。これにより、IDを手動でDoctrineエンティティに割り当てることができます、エンティティがAUTO、SEQUENCE、IDENTITY、またはUUIDの状態を使用している場合でも。

あなたは本番環境で使用すべきではありませんが、機能テストには本当に役立ちます。

ライブラリは、割り当てられたIDを持つエンティティを自動的に検出し、必要な場合にのみジェネレーターを置き換えます。インスタンスにIDが割り当てられていない場合、ライブラリは初期ジェネレーターにフォールバックします。

ジェネレーターの置き換えは、Doctrine EventListenerで行われ、フィクスチャーにコードを追加する必要はありません。

1
Tseho