2011-06-13 102 views
189

我想做一個簡單的例子,以瞭解如何從父表中刪除一行,並自動刪除使用Doctrine2在子表中匹配的行。關於刪除與doctrine2級聯

這裏有兩個實體我使用:

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

中的表正確的數據庫上創建的,但在刪除級聯選項,它不會被創建。我究竟做錯了什麼?

+0

您已經測試過的級聯是否執行正確反正第一,原始SQL實例中產生?也許Doctrine在代碼中而不是在數據庫中處理它們。 – Problematic 2011-06-13 16:19:20

回答

333

有兩種級聯的學說:

1)ORM水平 - 在聯想使用cascade={"remove"} - 這是在做的UnitOfWork並不會影響數據庫結構的計算。當你刪除一個對象時,UnitOfWork將迭代關聯中的所有對象並刪除它們。

2)數據庫級 - 在該協會的joinColumn使用onDelete="CASCADE" - 這將在數據庫中添加ON DELETE CASCADE外鍵列:

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

我也想指出的是,這樣,你有你的cascade = {「remove」}現在,如果刪除一個Child對象,這個級聯將刪除Parent對象。顯然不是你想要的。

+0

謝謝!我想我現在明白如何正確地做到這一點。您認爲哪種級聯方法是最佳做法,還是您會推薦? – rfc1484 2011-06-13 20:05:12

+3

我通常使用onDelete =「CASCADE」,因爲這意味着ORM必須做更少的工作,它應該有更好的性能。 – 2011-06-16 15:44:18

+44

我也是,但這要看情況。舉例來說,你有一個圖像庫與圖像。當您刪除圖庫時,也希望從磁盤中刪除圖像。如果您在圖像對象的delete()方法中實現該功能,則使用ORM進行級聯刪除將確保調用所有圖像的delte()函數,從而爲您節省執行檢查孤立圖像文件的cronjob的工作量。 – flu 2012-03-21 10:09:22

36

這裏是一個簡單的例子。聯繫人具有一對多關聯的電話號碼。當一個聯繫人被刪除時,我希望它的所有關聯電話號碼 也被刪除,所以我使用ON DELETE CASCADE。一對多/多對一關係由phone_numbers中的外鍵實現。

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; 

通過添加「ON DELETE CASCADE」的外鍵約束,PHONE_NUMBERS會自動在其相關的接觸是 隨之刪除。

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

現在,當聯繫人表中的一行被刪除時,其所有關聯的phone_numbers行都會自動刪除。

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

爲了實現學說同樣的事情,得到相同的DB-級 「ON DELETE CASCADE」 behavoir,配置與 的onDelete = 「CASCADE」選項@JoinColumn。

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

如果你現在要做的

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

你會看到相同的SQL將在

+3

它是正確的位置?刪除電話號碼不應刪除聯繫人。它是聯繫誰刪除應該觸發級聯。爲什麼然後在小孩/電話上放置級聯? – 2015-11-09 10:52:42

+1

@przemo_li這是正確的位置。聯繫人不知道電話號碼存在,因爲電話號碼有聯繫人的引用,並且聯繫人沒有對電話號碼的引用。所以如果一個聯繫人被刪除,一個電話號碼會引用一個不存在的聯繫人。在這種情況下,我們希望發生一些事情:觸發ON DELETE操作。我們決定級聯刪除,所以刪除電話號碼也是如此。 – marijnz0r 2016-05-11 08:16:39

+2

@przemi_li'onDelete =「cascade」'正確放置在實體中(在子級上),因爲這是** SQL級聯**,它放在子級上。只有Doctrine級聯('cascade = [「remove」]',這裏不使用*)放在父級上。 – Maurice 2016-10-06 11:21:19