Explicitly set Id with Doctrine when using "AUTO" strategy

43,444

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.

Share:
43,444

Related videos on Youtube

Eric
Author by

Eric

Well-versed with both front-end & back-end development, especially in regards to frameworks.

Updated on July 08, 2022

Comments

  • Eric
    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
      Wil Moore III about 13 years
      Eric, nevermind...I see what you are trying to do. You basically need a @GeneratedValue(strategy="ItDepends") :)
    • jmoz
      jmoz over 11 years
      The new solution does now allow me to set the id in a doctrine fixture. However, using $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMet‌​adata::GENERATOR_TYP‌​E_NONE); allows the id to be set and saved. (MySQL).
    • piotrekkr
      piotrekkr about 8 years
      That 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
    Eric over 11 years
    Thanks! Doctrine has improved a bit since this was first an issue, so I've accepted your answer & updated my original ticket accordingly.
  • nicolasbui
    nicolasbui over 11 years
    Thank you and I'm happy to help a bit as I can :)
  • Mantas
    Mantas over 10 years
    This is still relevant information and works for Doctrine 2.4.1, but the second line as mentioned by @gphilip should be removed.
  • Pavel Dubinin
    Pavel Dubinin almost 10 years
    will this set this generator permanently? Can I add one record with forced ID and then let it use autoincrement ids?
  • TiMESPLiNTER
    TiMESPLiNTER almost 9 years
    Does not work for Doctrine >2.5 because ClassMetadata is an interface and therefor can not have any constants.
  • Alexey B.
    Alexey B. almost 9 years
    There is a class ClassMetadata
  • Taz
    Taz over 8 years
    @gphilip The second line is important if you want it to work with associations.
  • Will B.
    Will B. almost 8 years
    Can simplify by using $metadata::GENERATOR_TYPE_NONE
  • bodo
    bodo about 7 years
    I 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).

Related