2011-06-13 5 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

답변

333

교리에 폭포 두 가지 종류가 있습니다

1) ORM 수준 - 협회에서 cascade={"remove"}를 사용합니다 - 이것은 UnitOfWork에에서 수행되고 데이터베이스 구조에 영향을 미치지 않는 계산이다. 개체를 제거하면 UnitOfWork는 연결의 모든 개체를 반복하여 제거합니다.

2) 데이터베이스 수준 - 협회의 joinColumn에 onDelete="CASCADE"을 사용하여 -이 데이터베이스에서 외래 키 열을 삭제 캐스케이드에 추가합니다 :

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

나는 또한 방법은 당신이 가진 것을 지적하려는 cascade = { "remove"} 지금은 Child 객체를 삭제하면이 연쇄 관계가 Parent 객체를 제거합니다. 분명히 당신이 원하는 것이 아닙니다.

+0

감사합니다. 나는 지금 그것을 올바르게하는 방법을 이해하고 있다고 생각합니다. 캐스케이드 방법 중 어떤 것이 베스트 프랙티스로 간주 되나요? 아니면 권장 할만한 방법입니까? – rfc1484

+3

나는 일반적으로 onDelete = "CASCADE"를 사용합니다. 왜냐하면 ORM은 작업량이 적어 성능이 조금 더 좋아야하기 때문입니다. –

+44

나는 또한 그렇게한다. 이미지가있는 이미지 갤러리가 있다고 가정 해 보겠습니다. 갤러리를 삭제할 때도 디스크에서 이미지를 삭제할 수 있습니다. 이를 이미지 객체의 delete() 메소드에 구현하면 ORM을 사용하는 계단식 삭제는 모든 이미지의 delte() 함수가 호출되도록하여 분리 된 이미지 파일을 확인하는 cronjob을 구현하는 작업을 저장합니다. – flu

36

다음은 간단한 예입니다. 연락처에는 하나 또는 여러 개의 연결된 전화 번호가 있습니다. 연락처가 삭제되면 관련 전화 번호 도 모두 삭제되기를 원하기 때문에 ON DELETE CASCADE를 사용합니다. one-to-many/many-to-one 관계는 phone_numbers의 외래 키로 구현됩니다. 관련 연락처가 삭제 이 때 외래 키 제약 조건에 "ON은 CASCADE 삭제"를 추가하여

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는 자동으로 삭제됩니다.

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 */ 

이 behavoir "DELETE CASCADE ON"같은 DB 수준을 얻기 위해, 교리에서 같은 일을 달성하기 위해, 당신은 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

올바른 위치에 배치 되었습니까? 전화 번호를 삭제해도 연락처가 삭제되어서는 안됩니다. 삭제가 캐스케이드를 트리거해야하는 사람과의 접촉입니다. 그렇다면 왜 어린이/전화에 캐스케이드를 배치해야합니까? –

+1

@przemo_li 올바른 위치에 있습니다. 전화 번호에는 연락처에 대한 참조가 있고 연락처에는 전화 번호에 대한 참조가 없기 때문에 연락처에 전화 번호가 있는지 알 수 없습니다. 연락처가 삭제되면 전화 번호에 존재하지 않는 연락처에 대한 참조가 있습니다. 이 경우에는 ON DELETE 동작을 트리거하는 것이 필요합니다. 삭제를 계단식으로하기로 결정하여 전화 번호도 삭제합니다. – marijnz0r

+2

@przemi_li 'onDelete = "cascade"는 자식에 배치 된 ** SQL 계단식 **이므로 엔티티 (하위 항목)에 올바르게 배치됩니다. Doctrine 캐스 케이 딩 ('cascade = [ "remove"]', * 여기 * 사용되지 않음) 만 부모에 위치합니다. – Maurice