Glesker
Glesker

Reputation: 23

Doctrine 2 - Cannot cascade delete entities with bidirectional relations

I'm getting a foreign constraint violation when trying to cascade delete entities having one-to-one and one-to-many bidirectional relations. Here are my four entities related this way : the "User" object may have zero or one "Contact". Contact may have zero or one "Address" and zero or many "Telephone". But "Contact" must be related to a "User" entity, as well as "Address" and "Telephon"e with a "Contact" entity.

My aim is, when I delete a "User" all the child objects are cascade deleted too ("Contact", "Address" and "Telephone"). However, when I delete a child object, I just want its reference id in the parent entity to be set to NULL.

I've tried several ways including the options onDelete="CASCADE" and onDelete=NULL but I still get the foreign constraint violation error.

User entity

class User
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * Contact
 *
 * @ORM\OneToOne(targetEntity="Contact", mappedBy="user", cascade={"persist", "remove"})
 * @ORM\JoinColumn(nullable=true)
 */
private $contact;

...

}

Contact entity

class Contact
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * Address
 *
 * @ORM\OneToOne(targetEntity="Address", mappedBy="contact", cascade={"persist", "remove"})
 * @ORM\JoinColumn(nullable=true)
 */
private $address;

/**
 * Telephones
 *
 * @ORM\OneToMany(targetEntity="Telephone", mappedBy="contact", cascade={"persist", "remove"})
 * @ORM\JoinColumn(nullable=true)
 */
private $telephones;

/**
 * User
 *
 * @ORM\OneToOne(targetEntity="User", inversedBy="contact", cascade={"persist"})
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
 */
private $user;

 /**
 * Constructeur
 */
public function __construct()
{
    $this->telephones = new ArrayCollection();
}

...

}

Address entity

class Address
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\OneToOne(targetEntity="Contact",  inversedBy="address", cascade={"persist"})
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false)
 */
private $contact;

...

}

Telephone entity

class Telephone
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\ManyToOne(targetEntity="Contact", inversedBy="telephones", cascade={"persist"})
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false)
 */
private $contact;

...

}

Upvotes: 2

Views: 3084

Answers (2)

Tom Corrigan
Tom Corrigan

Reputation: 394

Try this configuration. I have only made changes to the @ORM\JoinColumn annotations by adding onDelete="CASCADE". This uses the built in database cascading so you will need to update your schema. I have also removed some extraneous @ORM\JoinColumn(nullable=true) annotations on non owning sides of relations. These had no effect and were only misleading.

User entity

class User
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * Contact
 *
 * @ORM\OneToOne(targetEntity="Contact", mappedBy="user", cascade={"persist", "remove"})
 */
private $contact;

...

}

Contact entity

class Contact
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * Address
 *
 * @ORM\OneToOne(targetEntity="Address", mappedBy="contact", cascade={"persist", "remove"})
 */
private $address;

/**
 * Telephones
 *
 * @ORM\OneToMany(targetEntity="Telephone", mappedBy="contact", cascade={"persist", "remove"})
 */
private $telephones;

/**
 * User
 *
 * @ORM\OneToOne(targetEntity="User", inversedBy="contact", cascade={"persist"})
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
 */
private $user;

 /**
 * Constructeur
 */
public function __construct()
{
    $this->telephones = new ArrayCollection();
}

...

}

Address entity

class Address
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\OneToOne(targetEntity="Contact",  inversedBy="address", cascade={"persist"})
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
 */
private $contact;

...

}

Telephone entity

class Telephone
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\ManyToOne(targetEntity="Contact", inversedBy="telephones", cascade={"persist"})
 * @ORM\JoinColumn(name="contact_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
 */
private $contact;

...

}

Upvotes: 1

Bartłomiej Wach
Bartłomiej Wach

Reputation: 1986

$user->setContact(null); 
$contact->setUser(null);

$em->remove($user); 
$em->remove($contact);

Upvotes: 0

Related Questions