php - relations - En eliminar cascada con doctrine2
doctrine relations cascade (2)
Aquí está el ejemplo simple. Un contacto tiene uno o varios números de teléfono asociados. Cuando se elimina un contacto, quiero que también se eliminen todos sus números de teléfono asociados, así que uso ON DELETE CASCADE. La relación uno a muchos / muchos a uno se implementa con la clave externa en los números de teléfono.
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;
Al agregar "ON DELETE CASCADE" a la restricción de clave externa, los números de teléfono se eliminarán automáticamente cuando se elimine su contacto asociado.
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);
Ahora, cuando se elimina una fila en la tabla de contactos, todas sus filas de números de teléfono asociadas se eliminarán automáticamente.
DELETE TABLE contacts as c WHERE c.id=1; /* delete cascades to phone_numbers */
Para lograr lo mismo en Doctrine, para obtener el mismo comportamiento "ON DELETE CASCADE" a nivel de base de datos, configure @JoinColumn con la opción 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";
}
Si ahora lo haces
# doctrine orm:schema-tool:create --dump-sql
Verá que se generará el mismo SQL que en el primer ejemplo de SQL sin formato
Estoy tratando de hacer un ejemplo simple para aprender cómo eliminar una fila de una tabla principal y eliminar automáticamente las filas coincidentes en la tabla secundaria con Doctrine2.
Aquí están las dos entidades que estoy usando:
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;
}
Padre.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;
}
Las tablas se crean correctamente en la base de datos, pero la opción En eliminar cascada no se crea. ¿Qué estoy haciendo mal?
Hay dos tipos de cascadas en Doctrine:
1) Nivel de ORM: utiliza cascade={"remove"}
en la asociación: este es un cálculo que se realiza en UnitOfWork y no afecta la estructura de la base de datos. Cuando elimina un objeto, UnitOfWork itera sobre todos los objetos en la asociación y los elimina.
2) Nivel de la base de datos: utiliza onDelete="CASCADE"
en joinColumn de la asociación - esto agregará On Delete Cascade a la columna de clave externa en la base de datos:
@ORM/JoinColumn(name="father_id", referencedColumnName="id", onDelete="CASCADE")
También quiero señalar que la forma en que tiene su cascada = {"eliminar"} en este momento, si elimina un objeto secundario, esta cascada eliminará el objeto principal. Claramente no es lo que quieres.