Reputation: 18143
In my model I have
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{
/**
* @var lots
*
* @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain")
*/
private $lotes;
/**
* @ORM\PreRemove
*/
public function deleteAllLots()
{
$lots = $this->getLots();
foreach ($lots as $lot) {
$this->lots->removeElement($lot);
}
}
}
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Lot
{
/**
* @var Terrain
*
* @ORM\ManyToOne(targetEntity="Terrain", inversedBy="lots")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="terrain_id", referencedColumnName="id")
* })
*/
var $terrain
}
Lot
I'm trying to delete a terrain, but formerly i deleted all lots associated with it. when i try to delete a Terrain, i get an error SQLSTATE[23000]: Integrity constraint violation
Upvotes: 2
Views: 5422
Reputation: 317
Why don't you use a PostRemove instead of PreRemove? This approach gives you the deletion of the Terrain after Lots without the violation of database integrity. After Lots deleted, now you could safely remove the related Terrain.
/**
* @ORM\PostRemove
*/
public function deleteTerrain(\Doctrine\ORM\Event\LifecycleEventArgs $args)
{
$em = $args->getEntityManager();
$terrain = $this->getTerrain();
$em->remove($terrain);
$em->flush();
}
Upvotes: 0
Reputation: 790
The first code posted here doesn't also delete the data from the second table, it just nulls the reference and usually that should never be null if you only depend on one entity(you shouldn't have any old rows without any references in that table since there is nothing you can do with them in most cases, it will only keep that table full of stuff you don't need and screw you indexes).
From the doctrine 2 docs:
Lifecycle Callbacks are methods on the entity classes that are called when the event is triggered. They receive absolutely no arguments and are specifically designed to allow changes inside the entity classes state. Lifecycle Event Listeners are classes with specific callback methods that receives some kind >of EventArgs instance which give access to the entity, EntityManager or other relevant data.
Lifecycle Callbacks are only designed to change that entity, not the other entities it referees to. You must use an Lifecycle Event Listener and not a Lifecycle Callback. Using an Lifecycle Event Listener you can access the entity manager and delete those entities too. But the correct way to do this is to use delete cascade on the reference, this way doctrine can ignore the referenced objects(doesn't have to read them from the database just to delete them 1 operation later) and let the database handle them. You should just change this line:
@ORM\JoinColumn(name="terrain_id", referencedColumnName="id")
to this
@ORM\JoinColumn(name="terrain_id", referencedColumnName="id", onDelete="cascade")
You can find the documentation on the JoinColumn annotation here.
You can use the cascade="remove" param on the OneToMany
annotation as suggested here but that is much slower.
Upvotes: 1
Reputation: 649
You still have a reference from lots
to terrain
on deleting, that's why you have an Integrity constrait violation
.
Try this :
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{
/**
* @var lots
*
* @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain")
*/
private $lotes;
/**
* @ORM\PreRemove
*/
public function deleteAllLots()
{
$lots = $this->getLots();
foreach ($lots as $lot) {
$this->lots->removeElement($lot);
$lot->terrain = null; //$lot->setTerrain(null); will be better, try to add getters and setters
}
}
}
If you want to delete lots
from database, you should use cascade
property of OneToMany
relation (One-To-Many reference on doctrine-project.com), like this :
/**
* @ORM\Entity
* @ORM\HasLifecycleCallbacks()
*/
class Terrain
{
/**
* @var lots
*
* @ORM\OneToMany(targetEntity="Lot", mappedBy="terrain", cascade={"remove"})
*/
private $lotes;
}
On Terrain
remove, all lots will be removed.
Upvotes: 9