Explicitly set Id with Doctrine when using "AUTO" strategy
Solution 1
Although your solution work fine with MySQL, I failed to make it work with PostgreSQL as It's sequence based.
I've to add this line to make it work perfectly :
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
Solution 2
Perhaps what doctrine changed but now right way is:
$metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE);
Solution 3
In case the entity is part of a class table inheritance you need to change the id-generator in the class metadata for both entities (the entity you are persisting and the root entity)
Solution 4
New solution works fine only when ALL entities have id before insert. When one entity has ID and another one does not - new solution is failing.
I use this function for import all my data:
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 5
Solution for Doctrine 2.5 and MySQL
The "New solution" doesn't work with Doctrine 2.5 and MySQL. You have to use:
$metadata = $this->getEntityManager()->getClassMetaData(Entity::class);
$metadata->setIdGenerator(new AssignedGenerator());
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
However I can only confirm that for MySQL,because I haven't tried any other DBMS yet.
Related videos on Youtube
Eric
Well-versed with both front-end & back-end development, especially in regards to frameworks.
Updated on July 08, 2022Comments
-
Eric almost 2 years
My entity uses this annotation for it's ID:
/** * @orm:Id * @orm:Column(type="integer") * @orm:GeneratedValue(strategy="AUTO") */ protected $id;
From a clean database, I'm importing in existing records from an older database and trying to keep the same IDs. Then, when adding new records, I want MySQL to auto-increment the ID column as usual.
Unfortunately, it appears Doctrine2 completely ignores the specified ID.
New Solution
Per recommendations below, the following is the preferred solution:
$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());
Old Solution
Because Doctrine pivots off of the ClassMetaData for determining the generator strategy, it has to be modified after managing the entity in the EntityManager:
$this->em->persist($entity); $metadata = $this->em->getClassMetaData(get_class($entity)); $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE); $this->em->flush();
I just tested this on MySQL and it worked as expected, meaning Entities with a custom ID were stored with that ID, while those without an ID specified used the
lastGeneratedId() + 1
.-
Wil Moore III about 13 yearsEric, nevermind...I see what you are trying to do. You basically need a @GeneratedValue(strategy="ItDepends") :)
-
jmoz over 11 yearsThe new solution does now allow me to set the id in a doctrine fixture. However, using $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE); allows the id to be set and saved. (MySQL).
-
piotrekkr about 8 yearsThat new solution does not work in Symfony 3.0. I had to use
$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
-
-
Eric over 11 yearsThanks! Doctrine has improved a bit since this was first an issue, so I've accepted your answer & updated my original ticket accordingly.
-
nicolasbui over 11 yearsThank you and I'm happy to help a bit as I can :)
-
Mantas over 10 yearsThis is still relevant information and works for Doctrine 2.4.1, but the second line as mentioned by @gphilip should be removed.
-
Pavel Dubinin almost 10 yearswill this set this generator permanently? Can I add one record with forced ID and then let it use autoincrement ids?
-
TiMESPLiNTER almost 9 yearsDoes not work for Doctrine >2.5 because
ClassMetadata
is an interface and therefor can not have any constants. -
Alexey B. almost 9 yearsThere is a class ClassMetadata
-
Taz over 8 years@gphilip The second line is important if you want it to work with associations.
-
Will B. almost 8 yearsCan simplify by using
$metadata::GENERATOR_TYPE_NONE
-
bodo about 7 yearsI can confirm that this works with Symfony 3.2. What I did not expect however, was that the generator has to be set after executing
$em->persist($entity)
.