Brian
Brian

Reputation: 7155

Doctrine2: Unable to override generated value strategy?

I have a an entity with an ID as such:

/**
 * @ORM\Column(type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

I'm migrating data into this entity, and want to preserve existing keys. I looked at "Explicitly set Id with Doctrine when using "AUTO" strategy" and found that I should be able to do the following:

$newData = ... // array containing data to bring in

$newEntity = new MyEntity();
$newEntity->setId($newData['id']);
$newEntity->... // set other data fields

$em->persist($newEntity);
$metadata = $em->getClassMetadata('\CS\AcmeBundle\Entity\MyEntity');
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
$em->flush();

However, Doctrine is not using the provided ID. It's ignoring it when inserting. I've also tried this approach instead, since some people seemed to have had luck with it (even tried both):

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

But that doesn't change anything. ID's are still inserted automatically by the database. In the query log, I see that Doctrine isn't even attempting to insert the ID.

If I remove @ORM\GeneratedValue(strategy="AUTO") from MyEntity annotations, then the migration will respect the provided ID I give it. But I want to override it just during the migration.

I'm using Doctrine 2.4.2.

Upvotes: 2

Views: 2095

Answers (2)

Ahad Ali
Ahad Ali

Reputation: 418

The differences between GeneratedValue strategies

Inside your entity

Replace

@ORM\GeneratedValue(strategy="AUTO")

with

@ORM\GeneratedValue(strategy="NONE")

I am not sure whether you are using annotations or xml, or yml files. So better to change the xml or yml doctrine entity files inside your bundle config as well.

Upvotes: 0

Seth Battin
Seth Battin

Reputation: 2851

For this technique to work, you must use the second of these:

$metadata = $em->getClassMetadata('\CS\AcmeBundle\Entity\MyEntity');
$metadata = $em->getClassMetadata('CS\AcmeBundle\Entity\MyEntity');

The problem is that Doctrine will return the same class meta data values for both. They will both correctly identify the class file, read its annotations, etc. Obviously they are equivalent, except that one is an absolute namespace and the other is not.

But these strings will return different instances from getClassMetadata. Changes to one won't reflect in the other. If you want your intended technique to work, you must use the second form, because that is what UnitOfWork uses. It uses this normalization:

// \Doctrine\ORM\UnitOfWork->getCommitOrder()
...
$className = $this->em->getClassMetadata(get_class($entity))->name;
$class = $this->em->getClassMetadata($className);
...

Note that in the linked-to question, the solution uses get_class($entity). That is probably sufficient to get the correct behavior.


Even more detail: after a lot of stepping through code, I noticed that \Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory was memoizing both versions of the class name string in its private property $loadedMetadata. The version that was being used to actually flush the entities was the one without the leading slash, and I was editing the one with the leading slash.

Because both strings return the same data, I think this represents a bug in the implementation.

Upvotes: 1

Related Questions