doctrine2로 계단식 삭제시


227

부모 테이블에서 행을 삭제하고 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;
}

테이블은 데이터베이스에서 올바르게 작성되지만 캐스케이드 삭제시 옵션은 작성되지 않습니다. 내가 뭘 잘못하고 있죠?


캐스케이드가 올바르게 수행되는지 테스트 했습니까? 아마도 Doctrine은 데이터베이스 대신 코드로 처리합니다.
문제의

답변:


408

교리에는 두 종류의 계단식이 있습니다.

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

2) 데이터베이스 수준- onDelete="CASCADE"연결의 joinColumn에서 사용-데이터베이스 의 외래 키 열에 삭제시 캐스케이드가 추가됩니다.

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

또한 캐스케이드 = { "remove"}가있는 방식을 지적하고 싶습니다. Child 객체를 삭제하면이 캐스케이드가 Parent 객체를 제거합니다. 분명히 당신이 원하는 것이 아닙니다.


3
나는 일반적으로 onDelete = "CASCADE"를 사용합니다. 왜냐하면 ORM은 더 적은 작업을 수행해야하고 성능이 약간 향상되어야하기 때문입니다.
Michael Ridgway 2016 년

57
나도 그래도 달라. 예를 들어 이미지가 포함 된 이미지 갤러리가 있다고 가정합니다. 갤러리를 삭제하면 디스크에서도 이미지를 삭제하려고합니다. 이미지 객체의 delete () 메소드에서이를 구현하면 ORM을 사용한 계단식 삭제로 모든 이미지의 delte () 함수가 호출되어 고아 이미지 파일을 확인하는 크론 작업을 수행하는 작업이 줄어 듭니다.
flu

4
@Michael Ridgway 는 예를 들어 fosUser와 관련된 객체가있는 경우 onDelete뿐만 아니라 두 가지 설명을 모두 적용해야합니다 cascade = {"remove"}. 두 물체 모두 단독으로 존재해서는 안됩니다
Luke Adamczewski

17
@ORM\JoinColumn(onDelete="CASCADE")교리가 열 이름을 자동으로 처리하고 계속 처리하도록 할 수 있습니다 .
mcfedr

5
@dVaffection 좋은 질문입니다. 나는 onDelete="CASCADE"Doctrine cascade={"remove"}이 루트 엔티티를 제거하기 전에 관련 엔티티를 제거하기 때문에 아무런 영향을 미치지 않을 것이라고 생각합니다 ( 필수 ). 따라서 루트 엔터티가 삭제되면 삭제할 외래 관계가 없습니다 onDelete="CASCADE". 그러나 확실히 작은 테스트 사례를 만들고 실행중인 쿼리와 실행 순서를 확인하는 것이 좋습니다.
독감

50

다음은 간단한 예입니다. 연락처에는 일대 다 관련 전화 번호가 있습니다. 연락처가 삭제되면 관련된 모든 전화 번호도 삭제되기를 원하므로 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 */

Doctrine에서 동일한 작업을 수행하려면 동일한 DB 수준 "ON DELETE CASCADE"동작을 얻으려면 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 예제에서와 동일한 SQL이 생성됨을 알 수 있습니다.


4
올바른 배치입니까? 전화 번호를 삭제하면 연락처가 삭제되지 않아야합니다. 삭제가 캐스케이드를 트리거해야하는 담당자입니다. 그렇다면 왜 어린이 / 전화에 캐스케이드를 설치해야합니까?
przemo_li

1
@przemo_li 올바른 배치입니다. 전화 번호에는 연락처에 대한 참조가 있고 연락처에는 전화 번호에 대한 참조가 없기 때문에 연락처는 전화 번호가 존재한다는 것을 모릅니다. 따라서 연락처가 삭제되면 전화 번호에 존재하지 않는 연락처가 표시됩니다. 이 경우, ON DELETE 조치 트리거와 같은 일이 발생하기를 원합니다. 전화 번호도 삭제하기 위해 삭제 단계를 진행하기로 결정했습니다.
marijnz0r

3
@przemi_li는 onDelete="cascade"자식에있는 SQL cascading 이므로 엔터티 (자식)에 올바르게 배치됩니다 . 오직 교리 계단식 ( cascade=["remove"],되는 없습니다 여기에 사용)가 상위에 배치됩니다.
Maurice
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.