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


100

私のエンティティはその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


doctrineを使用して既存のレコードをインポートしていますか?
rojoca

2
エリック、気にしないで...あなたが何をしようとしているのか分かります。基本的には@GeneratedValue(strategy = "ItDepends")が必要です:)
Wil Moore III

1
これについて注意すべきことの1つは、 "isPostInsertGenerator" == trueではないIdジェネレータが既に実行されているように見えることです。IDの値は永続化後に変更できますが、シーケンス番号は失われます。
gview 2012

15
新しいソリューションでは、DoctrineフィクスチャにIDを設定できるようになりました。ただし、$ metadata-> setIdGeneratorType(\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE);を使用します。IDを設定および保存できます。(MySQL)。
jmoz

2
その新しいソリューションはSymfony 3.0では機能しません。私は使用しなければなり$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
ませんでした

回答:


51

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

完全に機能させるために、この行を追加する必要があります。

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

宜しくお願いします、


ありがとう!Doctrineは最初の問題以来少し改善されたので、私はあなたの回答を受け入れ、それに応じて元のチケットを更新しました。
エリック

ありがとうございました。できる限りサポートさせていただきます:)
nicolasbui

2
これはこのジェネレーターを永続的に設定しますか?強制IDを持つレコードを1つ追加して、自動インクリメントIDを使用させることはできますか?
Pavel Dubinin 2014年

1
これがSymfony 3.2で動作することを確認できます。しかし、私が期待していなかったことは、実行後にジェネレーターを設定する必要があること$em->persist($entity)でした。
bodo 2017

29

おそらく教義が変わったが今は正しい方法は:

$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);

1
これはまだ関連情報であり、Doctrine 2.4.1でも機能しますが、@ gphilipで言及されている2行目は削除する必要があります。
Mantas

ClassMetadataインターフェイスであり、そのため定数を持つことができないため、Doctrine> 2.5では機能しません。
TiMESPLiNTER 2015年

クラスがありClassMetadata
アレクセイB.

@gphilip 2行目は、関連付けで機能させる場合に重要です。
Taz

1
使用することで簡素化できます$metadata::GENERATOR_TYPE_NONE
fyrye

7

エンティティがクラステーブルの継承の一部である場合は、両方のエンティティ(永続化するエンティティとルートエンティティ)のクラスメタデータでidジェネレーターを変更する必要があります。


ルートエンティティを指定するだけで十分だと思います。metadatafactoryは、id戦略を決定するときに継承をチェックします。
セスバティン2014

実際、ルートエンティティにのみ追加すると、問題なく動作します。両方に追加すると、SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failsエラーが発生します。反対投票
ioleo

5

新しいソリューションは、すべてのエンティティが挿入前に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();
    }
}

4

Doctrine 2.5とMySQLのソリューション

「新しいソリューション」はDoctrine 2.5とMySQLでは動作しません。使用する必要があります:

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

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


1

Doctrineエンティティの将来のIDを設定するライブラリを作成しまし。キューに入れられたすべてのIDが消費されて影響を最小限に抑えると、元のID生成戦略に戻ります。このようなコードを繰り返さなくても済むように、これは単体テストの簡単なドロップインである必要があります。


1

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

あなたは生産にそれを使用するべきではありませんが、それは機能テストのために非常に便利です。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.