On delete cascade with doctrine2
Próbuję zrobić prosty przykład, aby dowiedzieć się, jak usunąć wiersz z tabeli nadrzędnej i automatycznie usunąć pasujące wiersze w tabeli podrzędnej za pomocą Doctrine2.
Oto dwa byty, których używam:
Dziecko.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;
}
Ojcze.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;
}
Tabele są poprawnie utworzone w bazie danych, ale opcja Delete Cascade nie jest utworzona. Co robię źle?
2 answers
W doktrynie istnieją dwa rodzaje kaskad:
1) poziom ORM-wykorzystuje cascade={"remove"}
w asocjacji - jest to obliczenia wykonywane w UnitOfWork i nie wpływa na strukturę bazy danych. Po usunięciu obiektu UnitOfWork będzie iteracją wszystkich obiektów w związku i usunie je.
2) Poziom bazy danych-używa onDelete="CASCADE"
na joinColumn asocjacji - spowoduje to dodanie Delete Cascade do kolumny klucza obcego w bazie danych:
@ORM\JoinColumn(name="father_id", referencedColumnName="id", onDelete="CASCADE")
Ja też chcę aby zwrócić uwagę, że sposób, w jaki masz swój cascade={"Usuń" } teraz, jeśli usuniesz obiekt potomny, ta kaskada usunie obiekt nadrzędny. Najwyraźniej nie tego chcesz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-10 08:33:47
Oto prosty przykład. Kontakt ma jeden do wielu powiązanych numerów telefonów. Gdy kontakt zostanie usunięty, chcę mieć wszystkie powiązane z nim numery telefonów aby również zostać usunięte, więc używam na DELETE CASCADE. Relacja one-to-many / many-To-one jest zaimplementowana za pomocą klucza obcego w 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;
Dodając" on DELETE CASCADE " do ograniczenia klucza obcego, phone_numbers zostaną automatycznie usunięte, gdy powiązany z nimi kontakt zostanie usunięte.
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);
Teraz, gdy wiersz w tabeli kontakty zostanie usunięta, wszystkie powiązane z nią wiersze phone_numbers zostaną automatycznie usunięte.
DELETE TABLE contacts as c WHERE c.id=1; /* delete cascades to phone_numbers */
Aby osiągnąć to samo w Doctrine, aby uzyskać ten sam dB-level "ON DELETE CASCADE", należy skonfigurować @JoinColumn z opcja 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";
}
If you now do
# doctrine orm:schema-tool:create --dump-sql
Zobaczysz, że ten sam SQL zostanie wygenerowany jak w pierwszym przykładzie raw-SQL
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-15 18:54:49