2011-06-13 19 views
189

Bir üst tablodaki bir satırın nasıl silineceğini öğrenmek ve Doctrine2'yi kullanarak alt tablodaki eşleşen satırları otomatik olarak silmek için basit bir örnek oluşturmaya çalışıyorum.Doctrine2 ile çağırma silme hakkında

Child.php: Burada

kullanıyorum iki varlıklardır

<?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 tabloları doğru veritabanı üzerinde oluşturulan

<?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; 
} 

ama Cascade'i Sil seçeneğinde oluşturulmaz. Neyi yanlış yapıyorum?

+0

ilk ham-SQL örnekte olduğu gibi oluşturulur göreceksiniz? Belki de doktrin onları veritabanında değil kodda ele alır. – Problematic

cevap

333

Doctrine'de cascades iki çeşidi vardır:

1) orm seviyesi - birlikte cascade={"remove"} kullanır - bu UnitOfWork yapılır ve veritabanı yapısını etkilemeyen bir hesaplamadır. Bir nesneyi kaldırdığınızda, UnitOfWork, ilişkilendirmedeki tüm nesneler üzerinde yineleyecek ve bunları kaldıracaktır.

2) Veritabanı seviyesi - Dernek joinColumn üzerinde onDelete="CASCADE" kullanır - bu veritabanındaki yabancı anahtar sütununa Sil Cascade Açık katacak:

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

Ben de yolu olduğunu işaret etmek istiyorum senin bir alt nesne silerseniz, bu çağlayan, Üst Nesneyi kaldıracaktır. Açıkçası istediğin gibi değil.

+0

Teşekkürler! Sanırım şimdi nasıl yapacağımı anlıyorum. Kaskad yöntemlerinden hangisini en iyi uygulamayı düşünürsünüz veya tavsiye edersiniz? – rfc1484

+3

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

+44

Ben de yapıyorum ama buna bağlı. Örneğin, resimler içeren bir resim galeriniz var. Galeriyi sildiğinizde, görüntülerin de diskten silinmesini istiyorsunuz. Bunu görüntü nesnesinin delete() yönteminde uygularsanız, ORM kullanarak basamaklı silme, tüm görüntünüzün delte() işlevlerinin çağrıldığından emin olursunuz, bu da size artık görüntü dosyalarını denetleyen cronjobs uygulamasının işini kurtarır. – flu

36

İşte basit bir örnek. Bir kişinin bir çok ilişkili telefon numarası vardır. Bir kişi silindiğinde, tüm ilişkili telefon numaraları 'un da silinmesini istiyorum, bu yüzden ON DELETE CASCADE kullanın. Bire-çok/çoktan bire bir ilişki telefon_numbersindeki yabancı anahtar tarafından uygulanır. bunların ilişkili kişi silinecek olduğunda yabancı anahtar kısıtlaması "AÇIK DELETE CASCADE" ekleyerek

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; 

, phone_numbers otomatik olarak silinecektir.

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

rehber tablosundaki bir satır silinir Şimdi, tüm ilişkili phone_numbers satırlar otomatik olarak silinir.

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

behavoir "SİL CASCADE ON" Aynı DB-konuma gelmesi, Doktrin aynı şeyi başarmak için, onDelete = "cascade" seçenek ile @JoinColumn yapılandırın.

<?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ız

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

aynı SQL sen çağlayan doğru neyse gerçekleştirmek ister test ettiniz

+3

Doğru yerleştirme mi? Telefon numarasını silme, kişiyi silmemelidir. Silme işleminin kaskatı tetiklemesi gereken kişi. Neden ardı ardına çocuk/telefona yerleştirilir? –

+1

@przemo_li Doğru yerleştirme. İlgili kişi telefon numaralarını bilmiyor, çünkü telefon numaralarının kişi için bir referansı var ve bir kişinin telefon numaralarına bir referansı yok. Böylece bir kişi silinirse, bir telefon numarasının mevcut olmayan bir kişiye referansı vardır. Bu durumda, bir şeylerin olmasını isteriz: ON DELETE eylemini tetikler. Telefon numaralarını silmek için silme işlemini kademelendirmeye karar verdik. – marijnz0r

+2

@przemi_li the onDelete = "çağlayan" 'çocuğa yerleştirilir (çocuk üzerinde). Bu, çocuğa yerleştirilen ** SQL basamaklı ** 'dır. Sadece Doktrin basamaklı ('* * * * *]' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' konulmaktadır – Maurice