228 votos

En cascada de eliminar con doctrine2

Intento hacer un ejemplo sencillo para aprender cómo eliminar una fila de una tabla primaria y eliminar automáticamente las filas coincidentes en la tabla secundaria usando 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;
}

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

Las tablas se crean correctamente en la base de datos, pero la opción de Delete Cascade no se crea. ¿Qué estoy haciendo mal?

411voto

Michael Ridgway Puntos 1796

Hay dos tipos de cascadas en la Doctrina:

1) ORM nivel - usa cascade={"remove"} en la asociación - este es un cálculo que se realiza en el UnitOfWork y no afecta a la estructura de base de datos. Al quitar un objeto, el UnitOfWork va a iterar sobre todos los objetos de la asociación y eliminarlos.

2) nivel de Base de datos - usa onDelete="CASCADE" en el de la asociación de joinColumn - 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 tiene su cascade={"quitar"} ahora, si elimina un objeto secundario, esta cascada se retire el objeto Primario. Claramente no es lo que usted desea.

50voto

Kurt Krueckeberg Puntos 617

Aquí es simplemente el ejemplo. Un contacto que se tiene de uno a varios números de teléfono asociados. Cuando un contacto se elimina, quiero que todos sus números de teléfono asociados también se eliminan, por lo que utilizar ON DELETE CASCADE. El uno-a-muchos/muchos-a-uno, la relación se lleva a cabo con la clave externa en la 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;

Mediante la adición de "ON DELETE CASCADE" a la restricción de clave externa, phone_numbers se eliminarán automáticamente cuando sus asociados de contacto es eliminados.

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 una fila de la tabla de contactos se eliminan todos sus asociados phone_numbers filas será automáticamente eliminado.

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

Para lograr lo mismo en la Doctrina, para obtener el mismo DB de nivel de "ON DELETE CASCADE" comportamiento, configurar el @JoinColumn con el onDelete="CASCADA" de la opción.

<?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;
     }
 } 
?>
$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 hacemos

# doctrine orm:schema-tool:create --dump-sql

usted verá que el mismo SQL se genera como en la primera, raw-SQL ejemplo

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X