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

तालिकाओं को डेटाबेस पर सही ढंग से बनाया गया है, लेकिन इसे डिलीट कास्केड विकल्प पर नहीं बनाया गया है। मैं क्या गलत कर रहा हूं?


क्या आपने परीक्षण किया है कि क्या कैस्केड किसी भी तरह से सही प्रदर्शन करता है? शायद डेटाबेस में कोड के बजाय डॉक्ट्रिन उन्हें संभालती है।
समस्यात्मक

जवाबों:


408

सिद्धांत में दो प्रकार के कैस्केड हैं:

1) ORM स्तर - cascade={"remove"}एसोसिएशन में उपयोग करता है - यह एक गणना है जो UnitOfWork में किया जाता है और डेटाबेस संरचना को प्रभावित नहीं करता है। जब आप किसी ऑब्जेक्ट को हटाते हैं, तो UnitOfWork एसोसिएशन में सभी ऑब्जेक्ट्स पर पुनरावृति करेगा और उन्हें हटा देगा।

2) डेटाबेस स्तर - onDelete="CASCADE"संघ के जुड़ाव पर उपयोग करता है - इससे डेटाबेस में विदेशी कुंजी कॉलम में डिलीट कैस्केड जुड़ जाएगा:

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

मैं यह भी बताना चाहता हूं कि जिस तरह से आपका कैस्केड = {"निकालें"} अभी है, यदि आप चाइल्ड ऑब्जेक्ट को हटाते हैं, तो यह कैस्केड पेरेंट ऑब्जेक्ट को हटा देगा। स्पष्ट रूप से वह नहीं जो आप चाहते हैं।


3
मैं आम तौर पर onDelete = "CASCADE" का उपयोग करता हूं क्योंकि इसका मतलब है कि ORM को कम काम करना पड़ता है और इसमें थोड़ा बेहतर प्रदर्शन होना चाहिए।
माइकल रिडगवे

57
मैं भी करता हूं लेकिन यह निर्भर करता है। उदाहरण के लिए कहें कि आपके पास छवियों के साथ एक छवि गैलरी है। जब आप गैलरी हटाते हैं तो आप चाहते हैं कि चित्र डिस्क से भी हटाए जाएं। यदि आप इसे लागू करते हैं, तो आपकी छवि के डिलीट () विधि में ओआरएम का उपयोग करके कैस्केडिंग डिलीट करने से यह सुनिश्चित हो जाएगा कि आपकी सभी छवि की डेल्ट () फ़ंक्शन को कॉल किया गया है, जो आपको अनाथ इमेज फाइल की जांच करने वाले क्रोनोजर को लागू करने के कार्य को बचाती है।
फ्लू

4
@ मिचेल रिडवे को कभी-कभी दोनों कथनों को लागू करना चाहिए - onDeleteसाथ ही cascade = {"remove"}उदाहरण के लिए जब आपके पास fosUser से संबंधित कोई वस्तु हो। दोनों वस्तुओं को अकेले मौजूद नहीं होना चाहिए
ल्यूक एडमच्यूस्की

17
ध्यान दें कि आप @ORM\JoinColumn(onDelete="CASCADE")अभी भी लिख सकते हैं और फिर भी सिद्धांत को कॉलम नामों को स्वचालित रूप से संभालने दें।
mcfedr 10

5
@dVaffection यह एक अच्छा सवाल है। मुझे लगता है कि रूट इकाई को हटाने से पहले onDelete="CASCADE"डॉक्ट्रिन cascade={"remove"}से संबंधित संस्थाओं को हटाने से कोई प्रभाव नहीं पड़ेगा । इसलिए जब मूल इकाई को हटा दिया जाता है तो कोई भी विदेशी संबंध नष्ट होने के लिए नहीं बचा है onDelete="CASCADE"। लेकिन यह सुनिश्चित करने के लिए कि मैं आपको सुझाव दूंगा कि आप एक छोटा सा टेस्ट केस बनाएं और निष्पादित होने वाले प्रश्नों और उनके निष्पादन के क्रम को देखें।
फ्लू

50

यहाँ सरल उदाहरण है। एक संपर्क में कई संबंधित फोन नंबर होते हैं। जब कोई संपर्क हटा दिया जाता है, तो मैं चाहता हूं कि उसके सभी संबद्ध फोन नंबर भी हटा दिए जाएं, इसलिए मैं DELETE 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;

विदेशी प्रमुख बाधा पर "ON DELETE CASCADE" जोड़कर, फोन_नोट्स स्वचालित रूप से हटा दिए जाएंगे जब उनका संबद्ध संपर्क हटा दिया जाएगा।

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

अब जब संपर्क तालिका में एक पंक्ति हटा दी जाती है, तो उसके सभी संबद्ध फ़ोन_नोट पंक्तियाँ स्वचालित रूप से हटा दी जाएंगी।

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

डॉक्ट्रिन में समान चीज़ को प्राप्त करने के लिए, उसी DB स्तर के "ON DELETE CASCADE" व्यवहारकर्ता को प्राप्त करने के लिए, आप @JoinColumn onDelete = "CASCADE" विकल्प से कॉन्फ़िगर करें ।

<?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 कार्रवाई को ट्रिगर करना। हमने विलोपन को रोकने का निर्णय लिया, इसलिए फ़ोन नंबर भी हटा दिए।
मारिजान ०१

3
@przemi_li को onDelete="cascade"इकाई में (बच्चे पर) सही ढंग से रखा गया है क्योंकि यह SQL कैस्केडिंग है , जिसे बच्चे पर रखा गया है। केवल डॉक्ट्रिन कैस्केडिंग (cascade=["remove"] , जिसका उपयोग यहां नहीं किया गया है) को माता-पिता पर रखा गया है।
मॉरिस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.