Doktrin2 ile kademeli silme


227

Bir üst tablodan bir satır silmek ve otomatik olarak Doctrine2 kullanarak alt tablodaki eşleşen satırları silmek öğrenmek için basit bir örnek yapmaya çalışıyorum.

İşte kullandığım iki varlık:

Child.php:

<?php

namespace Acme\CascadeBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="child")
 */
class Child {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @ORM\ManyToOne(targetEntity="Father", cascade={"remove"})
     *
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="father_id", referencedColumnName="id")
     * })
     *
     * @var father
     */
    private $father;
}

Father.php

<?php
namespace Acme\CascadeBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="father")
 */
class Father
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
}

Tablolar veritabanında doğru şekilde oluşturulur, ancak Basamaklama Silme seçeneğinde oluşturulmaz. Neyi yanlış yapıyorum?


Kaskadların yine de doğru çalışıp çalışmadığını test ettiniz mi? Belki de Doctrine bunları veritabanı yerine kodda ele alır.
Sorunlu

Yanıtlar:


408

Doktrin'de iki tür basamak vardır:

1) ORM seviyesi - cascade={"remove"}ilişkilendirmede kullanır - bu, UnitOfWork'ta yapılan ve veritabanı yapısını etkilemeyen bir hesaplamadır. Bir nesneyi kaldırdığınızda, UnitOfWork ilişkilendirmedeki tüm nesneler üzerinde yineleme yapar ve bunları kaldırır.

2) Veritabanı düzeyi - onDelete="CASCADE"ilişkilendirmenin joinColumn'undaki kullanımlar - bu, veritabanındaki yabancı anahtar sütununa On Delete Cascade öğesini ekler:

@ORM\JoinColumn(name="father_id", referencedColumnName="id", onDelete="CASCADE")

Ayrıca, şu anda cascade = {"remove"} yönteminize sahip olduğunuzu belirtmek istiyorum, bir Child nesnesini silerseniz, bu kaskat Ana nesneyi kaldıracaktır. Açıkçası ne istediğinizi değil.


3
Genelde onDelete = "CASCADE" kullanıyorum çünkü ORM'in daha az iş yapması gerektiği ve biraz daha iyi performans göstermesi gerektiği anlamına geliyor.
Michael Ridgway

58
Ben de yaparım ama duruma göre değişir. Diyelim ki resimler içeren bir resim galeriniz var. Galeriyi sildiğinizde, görüntülerin diskten de silinmesini istiyorsunuz. Bunu görüntü nesnenizin delete () yönteminde uygularsanız, ORM kullanarak basamaklı silme, tüm resminizin delte () işlevlerinin çağrıldığından emin olur, böylece artık resim dosyalarını kontrol eden cronjobs uygulama işinden tasarruf edersiniz.
grip

4
@Michael Ridgway bazen her iki ifadeleri uygulanmalıdır - onDeleteyanı sıra cascade = {"remove"}örneğin Sen fosUser ile ilgili bazı nesne varken. Her iki nesne de yalnız olmamalıdır
Luke Adamczewski

17
@ORM\JoinColumn(onDelete="CASCADE")Sütun adlarını otomatik olarak yazıp doktrin kullanmaya izin verebileceğinizi unutmayın .
mcfedr

5
@dVaffection Bu iyi bir soru. onDelete="CASCADE"Doktrin'in cascade={"remove"}kök varlığı kaldırmadan önce ilgili varlıkları kaldırdığından hiçbir etkisinin olmayacağını düşünüyorum . Bu nedenle, kök varlık silindiğinde silinecek dış ilişkiler kalmaz onDelete="CASCADE". Ama emin olmak için sadece küçük bir test senaryosu oluşturmanızı ve yürütülen sorgulara ve bunların yürütme sırasına bakmanızı öneririm.
grip

50

İşte basit bir örnek. Bir kişide birden çok ilişkili telefon numarası bulunur. Bir kişi silindiğinde, ilişkili tüm telefon numaralarının da silinmesini istiyorum, bu yüzden ON DELCE CASCADE kullanıyorum. Bire çok / çoktan bire ilişki phone_numbers içindeki yabancı anahtarla uygulanır.

CREATE TABLE contacts
 (contact_id BIGINT AUTO_INCREMENT NOT NULL,
 name VARCHAR(75) NOT NULL,
 PRIMARY KEY(contact_id)) ENGINE = InnoDB;

CREATE TABLE phone_numbers
 (phone_id BIGINT AUTO_INCREMENT NOT NULL,
  phone_number CHAR(10) NOT NULL,
 contact_id BIGINT NOT NULL,
 PRIMARY KEY(phone_id),
 UNIQUE(phone_number)) ENGINE = InnoDB;

ALTER TABLE phone_numbers ADD FOREIGN KEY (contact_id) REFERENCES \
contacts(contact_id) ) ON DELETE CASCADE;

Yabancı anahtar sınırlamasına "ON DELETE CASCADE" eklenirse, ilgili kişiler silindiğinde phone_numbers otomatik olarak silinir.

INSERT INTO table contacts(name) VALUES('Robert Smith');
INSERT INTO table phone_numbers(phone_number, contact_id) VALUES('8963333333', 1);
INSERT INTO table phone_numbers(phone_number, contact_id) VALUES('8964444444', 1);

Şimdi kişiler tablosundaki bir satır silindiğinde, ilişkili tüm phone_numbers satırları otomatik olarak silinecektir.

DELETE TABLE contacts as c WHERE c.id=1; /* delete cascades to phone_numbers */

Doktrin'de aynı şeyi elde etmek için, aynı DB düzeyinde "ON DELCE CASCADE" davranışını elde etmek için, @JoinColumn'u onDelete = "CASCADE" seçeneğiyle yapılandırırsınız .

<?php
namespace Entities;

use Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity
 * @Table(name="contacts")
 */
class Contact 
{

    /**
     *  @Id
     *  @Column(type="integer", name="contact_id") 
     *  @GeneratedValue
     */
    protected $id;  

    /** 
     * @Column(type="string", length="75", unique="true") 
     */ 
    protected $name; 

    /** 
     * @OneToMany(targetEntity="Phonenumber", mappedBy="contact")
     */ 
    protected $phonenumbers; 

    public function __construct($name=null)
    {
        $this->phonenumbers = new ArrayCollection();

        if (!is_null($name)) {

            $this->name = $name;
        }
    }

    public function getId()
    {
        return $this->id;
    }

    public function setName($name)
    {
        $this->name = $name;
    }

    public function addPhonenumber(Phonenumber $p)
    {
        if (!$this->phonenumbers->contains($p)) {

            $this->phonenumbers[] = $p;
            $p->setContact($this);
        }
    }

    public function removePhonenumber(Phonenumber $p)
    {
        $this->phonenumbers->remove($p);
    }
}

<?php
namespace Entities;

/**
 * @Entity
 * @Table(name="phonenumbers")
 */
class Phonenumber 
{

    /**
    * @Id
    * @Column(type="integer", name="phone_id") 
    * @GeneratedValue
    */
    protected $id; 

    /**
     * @Column(type="string", length="10", unique="true") 
     */  
    protected $number;

    /** 
     * @ManyToOne(targetEntity="Contact", inversedBy="phonenumbers")
     * @JoinColumn(name="contact_id", referencedColumnName="contact_id", onDelete="CASCADE")
     */ 
    protected $contact; 

    public function __construct($number=null)
    {
        if (!is_null($number)) {

            $this->number = $number;
        }
    }

    public function setPhonenumber($number)
    {
        $this->number = $number;
    }

    public function setContact(Contact $c)
    {
        $this->contact = $c;
    }
} 
?>

<?php

$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);

$contact = new Contact("John Doe"); 

$phone1 = new Phonenumber("8173333333");
$phone2 = new Phonenumber("8174444444");
$em->persist($phone1);
$em->persist($phone2);
$contact->addPhonenumber($phone1); 
$contact->addPhonenumber($phone2); 

$em->persist($contact);
try {

    $em->flush();
} catch(Exception $e) {

    $m = $e->getMessage();
    echo $m . "<br />\n";
}

Şimdi yaparsan

# doctrine orm:schema-tool:create --dump-sql

ilk SQL örneğindekiyle aynı SQL'in oluşturulacağını göreceksiniz


4
Yerleşim doğru mu? Telefon numarasını silmek kişiyi silmemelidir. Silme işleminin basamaklandırmayı tetiklemesi gereken kişidir. Öyleyse neden çağlayanı çocuk / telefona yerleştirmelisiniz?
przemo_li

1
@przemo_li Doğru yerleşim. Telefon numaralarının kişiye referansı olduğu ve kişinin telefon numaralarına referansı olmadığı için, kişi telefon numaralarının mevcut olduğunu bilmiyor. Dolayısıyla, bir kişi silinirse, bir telefon numarasının mevcut olmayan bir kişiye referansı vardır. Bu durumda, bir şey olmasını istiyoruz: ON DELETE eylemini tetikleme. Silme işlemini kademelendirmeye karar verdik, böylece telefon numaralarını da silmek için.
marijnz0r

3
@przemi_li onDelete="cascade"varlığa (alt öğeye) doğru yerleştirilir, çünkü bu alt öğeye yerleştirilen SQL basamaklıdır . Sadece Doktrin basamaklı ( cascade=["remove"], hangi değil burada kullanılan) ebeveyn yerleştirilir.
Maurice
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.