105 votes

Définissez explicitement l'identifiant avec Doctrine lors de l'utilisation de la stratégie "AUTO"

Mon entité utilise cette annotation pour son ID :

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

À partir d'une base de données propre, j'importe des enregistrements existants à partir d'une ancienne base de données et j'essaie de conserver les mêmes IDs. Ensuite, lors de l'ajout de nouveaux enregistrements, je veux que MySQL incrémente automatiquement la colonne ID comme d'habitude.

Malheureusement, il semble que Doctrine2 ignore complètement l'ID spécifié.


Nouvelle Solution

Conformément aux recommandations ci-dessous, voici la solution préférée :

$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());

Ancienne Solution

Parce que Doctrine se base sur les ClassMetaData pour déterminer la stratégie de génération, il doit être modifié après la gestion de l'entité dans l'EntityManager :

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

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

$this->em->flush();

J'ai simplement testé ceci sur MySQL et cela a fonctionné comme prévu, cela signifie que les entités avec un ID personnalisé ont été stockées avec cet ID, tandis que celles sans ID spécifié utilisaient le lastGeneratedId() + 1.

0 votes

Utilisez-vous doctrine pour importer les enregistrements existants?

0 votes

Non, malheureusement les données sont extraites via une API externe au format JSON, puis mappées aux classes d'entité.

0 votes

Eric, cela ne fonctionne-t-il pas avec @GeneratedValue(strategy="NONE")?

53voto

nicolasbui Points 221

Bien que votre solution fonctionne bien avec MySQL, j'ai échoué à la faire fonctionner avec PostgreSQL car elle est basée sur les séquences.

Je dois ajouter cette ligne pour la faire fonctionner parfaitement :

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

0 votes

Merci! La doctrine s'est améliorée un peu depuis que cela était initialement un problème, j'ai donc accepté votre réponse et mis à jour mon ticket original en conséquence.

0 votes

Merci et je suis heureux de pouvoir aider autant que possible :)

3 votes

Est-ce que cela définira ce générateur définitivement? Puis-je ajouter un enregistrement avec un ID forcé puis le laisser utiliser des IDs auto-incrémentés?

32voto

forgottenbas Points 5254

Peut-être que la doctrine a changé mais maintenant la bonne façon est:

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

1 votes

Ceci est toujours une information pertinente et fonctionne pour Doctrine 2.4.1, mais la deuxième ligne, comme mentionné par @gphilip, doit être supprimée.

0 votes

Ne fonctionne pas pour Doctrine >2.5 car ClassMetadata est une interface et ne peut donc pas avoir de constantes.

0 votes

Il y a une classe ClassMetadata

7voto

Weregoat Points 29

Si l'entité fait partie d'un héritage de table de classe, vous devez changer le générateur d'identifiant dans les métadonnées de classe pour les deux entités (l'entité que vous persistez et l'entité racine).

0 votes

Je pense que le cas est que vous avez seulement besoin de spécifier l'entité racine. La metadatafactory vérifie l'héritage lors de la détermination de la stratégie id.

0 votes

En fait, quand je l'ajoute uniquement à l'entité principale, cela fonctionne parfaitement. Quand je l'ajoute aux deux, j'obtiens des erreurs SQLSTATE[23000]: Violation de contrainte d'intégrité : 1452 Impossible d'ajouter ou de mettre à jour une ligne enfant : une contrainte de clé étrangère échoue. A été downvoté

6voto

La nouvelle solution fonctionne bien uniquement lorsque TOUS les entités ont un ID avant l'insertion. Lorsqu'une entité a un ID et qu'une autre n'en a pas, la nouvelle solution échoue.

J'utilise cette fonction pour importer toutes mes données :

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();
    }
}

4voto

fishbone Points 505

Solution pour Doctrine 2.5 et MySQL

La "Nouvelle solution" ne fonctionne pas avec Doctrine 2.5 et MySQL. Vous devez utiliser :

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

Cependant, je ne peux confirmer que pour MySQL, car je n'ai pas encore essayé d'autres SGBD.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X