Symfony 2.7, Doctrine cascade remove - symfony

I try to use the doctrine cascade={"remove"} but it doesn't work.
The situation :
I have announces that contains many pictures (one picture can refer to only one announce). I want that when i delete the announce from the DB it's remove the pictures too.
Here is my code :
Entity announce :
/**
* Announce
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\Announce\AnnounceRepository")
*/
class Announce
{
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Announce\PictureAnnounce", inversedBy="coverAnnounce")
* #ORM\JoinColumn(name="cover_picture_id", referencedColumnName="id", nullable=true)
*/
private $coverPicture;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Announce\PictureAnnounce", mappedBy="announce", cascade={"remove"})
*/
private $pictureAnnounces;
}
Entity pictureAnnounce :
/**
* AppBundle\Entity\Announce\PictureAnnounce
*
* #ORM\Table(name="picture_announce")
* #ORM\Entity(repositoryClass="AppBundle\Entity\Announce\PictureAnnounceRepository")
* #ORM\HasLifecycleCallbacks
*/
class PictureAnnounce
{
/**
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Announce\Announce", mappedBy="coverPicture", cascade={"persist"})
*/
private $coverAnnounce;
/**
* #var Announce $announce
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Announce\Announce", inversedBy="pictureAnnounces")
* #ORM\JoinColumn(name="announce_id", referencedColumnName="id", nullable=false)
*/
private $announce;
The code for the delete (i set coverPicture to null so i don't have a foreign key error when removing)
$announce->setCoverPicture(null);
$em->flush();
$em->remove($announce);
$em->flush();
But i have an error on foreign key
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
update a parent row: a foreign key constraint fails
PS : I can't found how to render correctly my code :s
PS : This workaround works, but isn't it the way the cascade work?
if(count($announce->getPictureAnnounces()) > 0) {
$pictureAnnounces = $announce->getPictureAnnounces();
foreach ($pictureAnnounces as $pictureAnnounce) {
$em->remove($pictureAnnounce);
}
$em->flush();
} // Then remove the announce

Related

Can't remove entities iterating over collection, but I can remove getting them directly from repository

My News entity is connected with Document entity through joiner NewsDocument entity with some extra fields: News -> NewsDocument <- Document
/**
* #ORM\Entity
* #ORM\Table(name="news_document")
* #UniqueEntity(
* fields={"news", "document"}
* )
*/
class NewsDocument
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Document", inversedBy="news")
* #ORM\JoinColumn(nullable=false)
*/
private $document;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\News", inversedBy="documents")
* #ORM\JoinColumn(nullable=false)
*/
private $news;
/**
* #ORM\Column(type="integer")
*/
private $position;
My News entity
/**
* #ORM\OneToMany(targetEntity="App\Entity\NewsDocument", mappedBy="news", cascade={"persist"})
* #ORM\OrderBy({"position" = "ASC"})
*/
private $documents;
public function getNewsDocuments()
{
return $this->documents;
}
When I tried to remove each NewsEntity iterating over documents field of my News entity, the NewsEntity wasn't removed from DB.
$news = $this->entityManager->getRepository(News::class)->find(26);
foreach($news->getNewsDocuments() as $newsDocument) {
$this->entityManager->remove($newsDocument);
}
$this->entityManager->remove($news);
$this->entityManager->flush();
News was deleted but NewsDocument not.
However the following code is able to remove both entities.
$news = $this->entityManager->getRepository(News::class)->find(26);
$newsDocument = $this->entityManager->getRepository(NewsDocument::class)->find(1);
$this->entityManager->remove($newsDocument);
$this->entityManager->remove($news);
$this->entityManager->flush();
I don't understand why first code isn't able to remove NewsDocument from DB.
I dumped $newsDocument variable in both cases and looked similar to each others.
I probably found a problem. My News entity uses SoftDeletable extension for Doctrine, but the joiner entity doesn't. I guess that extension tries to set a deleted_at flag to the relation, instead of deleting it permanently. This is why if I get joiner entity directly from repository works correctly.

Foreign key and cascade problems on delete Symfony4

An entity project can have many personnages, many chapitres, one highConcept for each project. And the user can have many projects. Then, when I want to remove a project I have this error message :
An exception occurred while executing 'DELETE FROM projets WHERE id = ?' with params [2]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (writtle.personnages, CONSTRAINT FK_286738A6C18272 FOREIGN KEY (projet_id) REFERENCES projets (id))
this is my entities:
Personnages Entity
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Chapitre", mappedBy="personnages")
* #ORM\JoinColumn(name="projet_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $chapitres;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Projets", inversedBy="personnages")
* #ORM\JoinColumn(name="projet_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $projet;
Projet entity
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="projets")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Personnages", mappedBy="projet")
*/
private $personnages;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Chapitre", mappedBy="projet", cascade={"remove"})
* #ORM\joinColumn(onDelete="SET NULL")
*/
private $chapitres;
Chapitre entity
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Projets", inversedBy="chapitres")
*/
private $projet;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Personnages", inversedBy="chapitres")
*/
private $personnages;
HighConcept
/**
* #ORM\OneToOne(targetEntity="App\Entity\Projets", cascade={"persist", "remove"})
*/
private $projet;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="highconcepts")
*/
private $user;
User entity
/**
* #ORM\OneToMany(targetEntity="App\Entity\Projets", mappedBy="user")
*/
private $projets;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Highconcept", mappedBy="user")
*/
private $highconcepts;
I don't know how I can relate this, I tried some things like JoinColumn ondelete cascade...
I see some topics, but I think I use it correctly.
Considering that you want to remove all the entities contained in your project I suggest you to use the orphanRemoval option as explained in the Doc.
There is another concept of cascading that is relevant only when removing entities from collections. If an Entity of type A contains references to privately owned Entities B then if the reference from A to B is removed the entity B should also be removed, because it is not used anymore.
making the members of your entity look like:
/**
* #ORM\OneToMany(targetEntity="App\Entity\Personnages", mappedBy="projet", orphanRemoval=true)
*/
private $personnages;

Symfony defining Foreign key

This is my ERD
I need help with how to associate the foreign keys with my entities
from what i know is that it has to something with this kind of code:
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
TL;DR I made entities but now i need to associate the foreign keys (image) but i don't know how.
/**
* #ORM\ManyToOne(targetEntity="klant", mappedBy="Bestellingorder")
*/
private $klanten;
/**
* #ORM\OneToMany(targetEntity="Bestellingorder", inversedBy="klanten")
* #ORM\JoinColumn(name="Bestellingorder_id", referencedColumnName="id")
*/
private $Bestellingorder;
[Creation Error] The annotation #ORM\ManyToOne declared on property TuinBundle\
Entity\Bestellingorder::$klanten does not have a property named "mappedBy". Ava
ilable properties: targetEntity, cascade, fetch, inversedBy
stuck here.
Try with that :
/**
* #ORM\ManyToOne(targetEntity="klant", inversedBy="Bestellingorder")
* #ORM\JoinColumn(name="klant_id", referencedColumnName="id")
*/
private $klanten;
/**
* #ORM\OneToMany(targetEntity="Bestellingorder", mappedBy="klanten")
*/
private $Bestellingorder;
But i don't understand why you have sometimes "klant" or "klanten", didn't make sense for me. What is the name of your entity ? klant or klanten ?

Integrity constraint violation 1451 between entities

I've got these entities (relevant part for the problem) :
/**
* Criterion
*
* #ORM\Table(name="innova_stepcondition_criterion")
* #ORM\Entity(repositoryClass="Innova\PathBundle\Repository\CriterionRepository")
*/
class Criterion implements \JsonSerializable
{
/**
* Criteriagroup
* #var \Innova\PathBundle\Entity\Criteriagroup
*
* #ORM\ManyToOne(targetEntity="Innova\PathBundle\Entity\Criteriagroup", inversedBy="criteria", cascade={"all"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(onDelete="SET NULL")
* })
*/
protected $criteriagroup;
}
and
/**
* Criteriagroup
*
* #ORM\Table(name="innova_stepcondition_criteriagroup")
* #ORM\Entity(repositoryClass="Innova\PathBundle\Repository\CriteriagroupRepository")
*/
class Criteriagroup implements \JsonSerializable
{
/**
* Criteria linked to the criteriagroup
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="Innova\PathBundle\Entity\Criterion", mappedBy="criteriagroup", indexBy="id", cascade={"persist", "remove"})
*/
protected $criteria;
}
When i try to delete a criteriagroup, i want to delete the attached criterion. I've got this error :
An exception occurred while executing 'DELETE FROM innova_stepcondition_criteriagroup WHERE id = ?' with params [1]: SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`claroline_path`.`innova_stepcondition_criteriagroup`, CONSTRAINT `FK_F33A94EA727ACA70` FOREIGN KEY (`parent_id`) REFERENCES `innova_stepcondition_criteriagroup` (`id`))
So i've read some post like this our this one and the suggested solution is the solution is to use a onDelete="SET NULL" on the joinColumn in the ManyToOne side, which i did. But i still get this error.
What could be wrong ?
Thank you
EDIT :
#RaulFerriz : Thank you for the answer. If i try your modifications : with cascade={"persist"} instead of cascade={"all"} in Criterion, i still have the same error.
But if i remove entirely the cascade in Criterion, i have :
A new entity was found through the relationship \u0027Innova\\PathBundle\\Entity\\Criterion#criteriagroup\u0027 that was not configured to cascade persist operations for entity: Innova\\PathBundle\\Entity\\Criteriagroup#0000000034dfa7b200000000f76198d8. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={\u0022persist\u0022}). If you cannot find out which entity causes the problem implement \u0027Innova\\PathBundle\\Entity\\Criteriagroup#__toString()\u0027 to get a clue`
that seems to mean the cascade={"persist"} is needed.
I don't knwon what to try next.
I do a doctrine:schema:update after each modification.
Your problem is not in your relation Criteriagroup and Criteria.
If you check the SQL error, the constraint that failed is for the field parent_id. Which is part of your Criteriagroup / Criteriagroup relation.
I put here the definition of your buggy relation, if someone else need this :
/**
* Parent criteriagroup
* #var \Innova\PathBundle\Entity\Criteriagroup
*
* #ORM\ManyToOne(targetEntity="Criteriagroup", inversedBy="children", cascade={"all"})
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
/**
* Children criteriagroup
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="Criteriagroup", mappedBy="parent", indexBy="id", cascade={"persist", "remove"})
* #ORM\OrderBy({"order" = "ASC"})
*/
protected $children;
In order to make this work, you need to replace :
#ORM\JoinColumn(name="parent_id", referencedColumnName="id")
by
#ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
By adding this, when you will delete a CriteriaGroup, all children CriteriaGroup will be deleted. If you don't want to delete children, just add onDelete="SET NULL".
Hope it helps.
As you have defined the entities, when you try to remove a "Criterion" the entity Criteriongroup will also be deleted. That is not what you are attempting to achieve.
Try with this
/**
* Criterion
*
* #ORM\Table(name="innova_stepcondition_criterion")
* #ORM\Entity(repositoryClass="Innova\PathBundle\Repository\CriterionRepository")
*/
class Criterion implements \JsonSerializable
{
/**
* Criteriagroup
* #var \Innova\PathBundle\Entity\Criteriagroup
*
* #ORM\ManyToOne(targetEntity="Innova\PathBundle\Entity\Criteriagroup", inversedBy="criteria")
* #ORM\JoinColum(referencedColumnName="id")
*/
protected $criteriagroup;
}
And this
/**
* Criteriagroup
*
* #ORM\Table(name="innova_stepcondition_criteriagroup")
* #ORM\Entity(repositoryClass="Innova\PathBundle\Repository\CriteriagroupRepository")
*/
class Criteriagroup implements \JsonSerializable
{
/**
* Criteria linked to the criteriagroup
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="Innova\PathBundle\Entity\Criterion", mappedBy="criteriagroup", indexBy="id", cascade={"persist", "remove"})
*/
protected $criteria;
}
Basically, clean up Criterion entity from cascading all, at most I suppose you will need {cascade="persist"} at this entity that means: when Criterion is persisted, persist also Criteriagroup linked with it.

Symfony2 OneToMany relationship - remove action isn't called

I'm using Symfony 2.4.6 and I'm trying to use OneToMany relationship to manage images added to a banner.
I did read a lot about the deletion of a child element (setting orphanRemoval, adding 'remove' to cascade) but none of those worked for me. What I've noticed is that remove actions isn't called at all on update.
I have 2 classes, Banner and BannerFile and using collection field type for adding images and it seems to work OK except the delete action.
class Banner
{
/.../
/**
* #ORM\OneToMany(targetEntity="BannerFile", cascade={"persist", "remove"}, mappedBy="banner", orphanRemoval=true)
*/
private $bannerFiles;
/.../
/**
* Remove bannerFiles
*
* #param BannerFile $bannerFiles
*/
public function removeBannerFile(BannerFile $bannerFiles)
{
$this->bannerFiles->removeElement($bannerFiles);
}
}
class BannerFile
{
/.../
/**
* #var integer $banner
*
* #ORM\ManyToOne(fetch="EXTRA_LAZY", inversedBy="bannerFiles", targetEntity="Banner")
* #ORM\JoinColumn(name="banner_id", nullable=false, onDelete="CASCADE", referencedColumnName="id")
*/
private $banner;
/.../
}
My problem is that the removeBannerFile isn't called.
Thanks for any help.
This happens if you use a database table engine type that does not support foreign keys (such as MyISAM).
Change your engine type to InnoDB, then run the following code to update your database schema.
php console doctrine:schema:update
Add cascade to the ManyToOne definition:
class BannerFile
{
/.../
/**
* #var integer $banner
*
* #ORM\ManyToOne(fetch="EXTRA_LAZY", inversedBy="bannerFiles", targetEntity="Banner", cascade={"remove"})
* #ORM\JoinColumn(name="banner_id", nullable=false, onDelete="CASCADE", referencedColumnName="id")
*/
private $banner;
/.../
}

Resources