"OTOMATİK" stratejisini kullanırken Doktrin ile kimliği açıkça ayarlayın


101

Varlığım, kimliği için şu ek açıklamayı kullanıyor:

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

Temiz bir veritabanından, eski bir veritabanındaki mevcut kayıtları içe aktarıyorum ve aynı kimlikleri tutmaya çalışıyorum. Ardından, yeni kayıtlar eklerken, MySQL'in her zamanki gibi Kimlik sütununu otomatik olarak artırmasını istiyorum.

Maalesef, Doctrine2 belirtilen kimliği tamamen görmezden geliyor gibi görünüyor.


Yeni Çözüm

Aşağıdaki önerilere göre tercih edilen çözüm şu şekildedir:

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

Eski Çözüm

Doctrine, jeneratör stratejisini belirlemek için ClassMetaData'nın dışına çıktığı için, EntityManager'da varlık yönetildikten sonra değiştirilmesi gerekir:

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

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

$this->em->flush();

Bunu MySQL üzerinde test ettim ve beklendiği gibi çalıştı, yani özel bir kimliği olan Varlıklar bu kimlikle saklanırken, kimliği belirtilmemiş olanlar lastGeneratedId() + 1.


Mevcut kayıtları içe aktarmak için doktrin kullanıyor musunuz?
rojoca

2
Eric, boşver ... Ne yapmaya çalıştığını anlıyorum. Temelde bir @GeneratedValue'ye ihtiyacınız var (strateji = "ItDepends") :)
Deyon Samuel Washington

1
Bununla ilgili not edilmesi gereken bir nokta, "isPostInsertGenerator" == true olmayan Id üreteçlerinin zaten çalışmış olacağıdır. Kalıcı süreden sonra kimliğin değerini değiştirebilirsiniz, ancak bir sıra numarasını kaybedersiniz.
gview

15
Yeni çözüm artık bir doktrin fikstüründe kimliği belirlememe izin veriyor. Ancak, $ metadata-> setIdGeneratorType (\ Doctrine \ ORM \ Mapping \ ClassMetadata :: GENERATOR_TYPE_NONE) kullanarak; kimliğin ayarlanmasına ve kaydedilmesine izin verir. (MySQL).
jmoz

2
Bu yeni çözüm Symfony 3.0'da çalışmıyor. Ben kullanmak zorunda$metadata = $this->getEntityManager()->getClassMetaData(User::class); $metadata->setIdGenerator(new AssignedGenerator()); $metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
piotrekkr

Yanıtlar:


52

Çözümünüz MySQL ile iyi çalışsa da, sıra tabanlı olduğu için PostgreSQL ile çalışmasını sağlayamadım.

Mükemmel çalışmasını sağlamak için bu satırı eklemeliyim:

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

Saygılarımla,


Teşekkürler! Doktrin, bu ilk sorun olduğundan bu yana biraz gelişti, bu yüzden cevabınızı kabul ettim ve orijinal biletimi buna göre güncelledim.
Eric

Teşekkürler ve elimden geldiğince yardımcı olmaktan mutluluk
duyarım

3
bu, bu jeneratörü kalıcı olarak ayarlayacak mı? Zorunlu kimliğe sahip bir kayıt ekleyip otomatik artırma kimliklerini kullanmasına izin verebilir miyim?
Pavel Dubinin

1
Bunun Symfony 3.2 ile çalıştığını doğrulayabilirim. Ancak beklemediğim şey, jeneratörün çalıştırıldıktan sonra ayarlanması gerektiğiydi $em->persist($entity).
bodo

30

Belki de doktrin ne değişti ama şimdi doğru yol:

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

1
Bu hala geçerli bilgilerdir ve Doctrine 2.4.1 için çalışmaktadır, ancak @gphilip tarafından belirtildiği gibi ikinci satır kaldırılmalıdır.
Mantas

Doctrine> 2.5 için çalışmaz çünkü ClassMetadatabir arayüzdür ve dolayısıyla herhangi bir sabiti olamaz.
TiMESPLiNTER


@gphilip İkinci satır derneklerle çalışmasını istiyorsanız önemlidir .
Taz

1
Kullanarak sadeleştirebilir$metadata::GENERATOR_TYPE_NONE
Will B.

7

Varlığın bir sınıf tablosu mirasının parçası olması durumunda, her iki varlık için sınıf meta verilerindeki id-oluşturucuyu değiştirmeniz gerekir (devam ettirdiğiniz varlık ve kök varlık)


Sanırım durum, yalnızca kök varlığı belirtmeniz gerektiğidir. Metadatafactory, id stratejisini belirlerken kalıtımı kontrol eder.
Seth Battin

Aslında, onu yalnızca kök varlığa eklediğimde kusursuz çalışıyor. Her ikisine de eklediğimde SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failshatalar alıyorum . Olumsuz oy verildi
ioleo

5

Yeni çözüm, yalnızca TÜM varlıkların eklenmeden önce kimliği olduğunda iyi çalışır. Bir varlığın kimliği varsa ve diğeri yoksa - yeni çözüm başarısız oluyor.

Tüm verilerimi içe aktarmak için bu işlevi kullanıyorum:

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 ve MySQL için Çözüm

"Yeni çözüm" Doctrine 2.5 ve MySQL ile çalışmıyor. Kullanmalısınız:

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

Ancak bunu yalnızca MySQL için doğrulayabilirim, çünkü henüz başka bir DBMS denemedim.


1

Doctrine varlıkları için gelecekteki kimlikleri ayarlamak için bir kitaplık oluşturdum . Etkiyi en aza indirmek için sıraya alınan tüm kimlikler tüketildiğinde orijinal kimlik oluşturma stratejisine geri döner. Bunun gibi kodun tekrarlanmasına gerek kalmaması için birim testleri için kolay bir giriş olmalıdır.


1

Villermen çalışmasından esinlenerek , varlık AUTO, SEQUENCE, IDENTITY veya UUID kategorilerini kullansa bile ID'leri bir Doctrine varlığına manuel olarak atamanıza izin veren tseho / doctrine-atanmış-kimlik kitaplığını oluşturdum .

Sen üretimde kullanmak asla ama fonksiyonel testler için gerçekten yararlıdır.

Kitaplık, atanmış bir kimliği olan varlıkları otomatik olarak algılayacak ve oluşturucuyu yalnızca gerektiğinde değiştirecektir. Bir örneğin atanmış bir kimliği olmadığında kütüphane ilk oluşturucuya geri dönecektir.

Jeneratörün değiştirilmesi bir Doctrine EventListener'da gerçekleşir, armatürlerinize herhangi bir ek kod eklemenize gerek yoktur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.