[英]On delete cascade with 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;
}
父親.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;
}
表已在數據庫上正確創建,但未創建 On Delete Cascade 選項。 我究竟做錯了什么?
Doctrine中有兩種級聯:
1) ORM 級別 - 在關聯中使用cascade={"remove"}
- 這是在 UnitOfWork 中完成的計算,不會影響數據庫結構。 當您刪除 object 時,UnitOfWork 將遍歷關聯中的所有對象並刪除它們。
2) 數據庫級別 - 在關聯的 joinColumn 上使用onDelete="CASCADE"
- 這會將 On Delete Cascade 添加到數據庫中的外鍵列:
@ORM\JoinColumn(name="father_id", referencedColumnName="id", onDelete="CASCADE")
我還想指出,您現在擁有 cascade={"remove"} 的方式,如果您刪除子 object,此級聯將刪除父 object。 顯然不是你想要的。
這是一個簡單的例子。 一個聯系人有一對多關聯的電話號碼。 刪除聯系人時,我希望也刪除其所有關聯的電話號碼,因此我使用 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
雖然在級聯上刪除的正確方法是使用@Michael Ridgway 答案,但也有可能聽做 doctrine 事件來做同樣的事情。
為什么? 好吧,您可能希望在刪除父實體時做一些額外的事情,可能對某些實體使用軟刪除或硬刪除其他實體。 您還可以將他的孩子重新影響到另一個實體,以防您想保留它並將其影響到父實體等...
所以這樣做的方法是監聽doctrine 事件 preRemove 。
preRemove - 在執行該實體的相應 EntityManager 刪除操作之前,該實體發生 preRemove 事件。 DQL DELETE 語句不調用它。
請注意,只有在使用->remove
時才會調用此事件。
首先創建您的事件訂閱者/偵聽器以收聽此事件:
<?php
namespace App\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use App\Repository\FatherRepository;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use App\Entity\Father;
use App\Entity\Child;
class DoctrineSubscriber implements EventSubscriber
{
private $fatherRepository;
public function __construct(FatherRepository $fatherRepository)
{
$this->fatherRepository = $fatherRepository;
}
public function getSubscribedEvents(): array
{
return [
Events::preRemove => 'preRemove',
];
}
public function preRemove(LifecycleEventArgs $args)
{
$entity = $args->getObject();
if ($entity instanceof Father) {
//Custom code to handle children, for example reaffecting to another father:
$childs = $entity->getChildren();
foreach($childs as $child){
$otherFather = $this->fatherRepository->getOtherFather();
child->setFather($otherFather);
}
}
}
}
並且不要忘記將此 EventSubscriber 添加到您的服務中。yaml
App\EventSubscriber\DoctrineSubscriber:
tags:
- { name: doctrine.event_subscriber }
在此示例中,父親仍將被刪除,但孩子不會因為有了新父親而被刪除。 例如,如果實體Father
添加其他家庭成員,我們可以將孩子重新影響到家庭中的其他人。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.