(doctrine2 + symfony2) cascading remove : integrity constraint violation 1451 - symfony

First, sorry for my poor English...
I got four entities : User, Application, Bundle & Entity. Here are their relations (with cascading persist & remove, see code below) :
User 1-n Application
Application 1-n Bundle
Bundle 1-n Entity
It's working fine. But an User can have two of his entities as default, and I need to access them directly.
So I add on User two fields, entity1 & entity2, with a 1-1 relation. And now my app crashes :
An exception occurred while executing 'DELETE FROM bundle WHERE id = ?' with params {"1":13}:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`misc`.`entity`, CONSTRAINT `FK_E284468F1FAD9D3` FOREIGN KEY (`bundle_id`) REFERENCES `bundle` (`id`))
I tried several things, including those founded in this post, but I wasn't able to fix it.
Any help be welcome, thanks in advance.
EDIT : I need to point out that User->Entity relations are optionnal : User's entity1 & entity2 can be null. The error happens even if they are null both.
Here are my entities definitions :
# User :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id")
*/
private $entity2;
#Application :
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
#Bundle :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity :
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id")
*/
protected $bundle;

So, thanks to this French forum, I fixed the problem.
I needed to add nullable=true & onDelete="SET NULL" in #ORM\JoinColumn
Here is the workable configuration, maybe it will help someone :
#User.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $applications;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity1;
/**
* #ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
private $entity2;
#Application.
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $bundles;
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $user;
#Bundle.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
* #ORM\JoinColumn(name="application_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $application;
/**
* #ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
* #ORM\OrderBy({"name" = "ASC"})
*/
protected $entitys;
#Entity.
/**
* #ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
* #ORM\JoinColumn(name="bundle_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected $bundle;

Use onDelete="CASCADE" if you are using annotation
/**
* #ORM\ManyToOne(targetEntity="Report", inversedBy="responses")
* #ORM\JoinColumn(name="reportId", referencedColumnName="id",onDelete="CASCADE")
*/
Use onDelete: CASCADE if you are using yml
joinColumn:
name: pid
referencedColumnName: id
onDelete: CASCADE

onDelete="CASCADE" also works fine. But don't forget to run app/console doctrine:schema:update --force before DB level changes will take an effect.

orphanRemoval some times doesn't work because it depends on (gets schedulled in) PersistencCollection. And we might be calling ArrayCollection#removeElement().
Following is a snippet of the PersistencCollection#remove()
if ($this->association !== null &&
$this->association['type'] & ClassMetadata::TO_MANY &&
$this->owner &&
$this->association['orphanRemoval']) {
$this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
}
and ArrayCollection does not do that.

Related

How to update OneToMany ressource

I am a beginner in API platform. I am working with two entities Author and Book with a OneToMany relationship:
a user can have several books
a book can belong to only one Author.
I've tried POST, GET and DELETE methods, they work. However, the PUT method does not work and returns this error:
"hydra:title": "An error occurred",
"hydra:description": **"An exception occurred while executing 'UPDATE Book SET author_id = ? WHERE id = ?' with params [null, 1]:\n\nSQLSTATE[23000]:
Integrity constraint violation: 1048 Column 'author_id' cannot be null"
Here's my code:
/**
* #ORM\Entity(repositoryClass="App\Repository\AuthorRepository")
* #ApiResource(
* normalizationContext={"groups"={"book:output"}},
* denormalizationContext={"groups"={"book:input"}}
* )
*/
class Author
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"book:input"})
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $firstname;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $lastname;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Book", mappedBy="author", cascade={"all"})
* #Groups({"book:output", "book:input"})
*/
private $books;
/**
* #ORM\Entity(repositoryClass="App\Repository\BookRepository")
* #ApiResource()
*/
class Book
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"book:output", "book:input"})
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Groups({"book:output", "book:input"})
*/
private $name;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Author", inversedBy="books")
* #ORM\JoinColumn(nullable=false)
*/
private $author;
You seem like passing a null id for an updated entity which is essential for the ORM to know.

How to listen cascade remove with Event suscriber / Listner with Symfony 4

I would need to listen to the cascade remove event.
Let me explain :
I have an Entity Brand an Entity Video.
For a video, I have One Brand, gallery with gallery items and each galleryItem have a media
Entity Video :
/**
* #var Brand
* #Assert\NotNull()
*
* #ORM\ManyToOne(targetEntity="Brand", fetch="EAGER")
* #JoinColumn(name="brand_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
private $brand;
/**
* #var Gallery
* #ORM\OneToOne(targetEntity="Gallery", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="attachments_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $attachments;
Entity Gallery :
/**
* Unidirectional OneToMany relationship.
*
* #var GalleryItem[]
* #ORM\ManyToMany(targetEntity="GalleryItem", cascade={"persist", "remove"}, fetch="EAGER", orphanRemoval=true)
* #ORM\JoinTable(name="gallery_galleryitems",
* joinColumns={#ORM\JoinColumn(onDelete="CASCADE")},
* inverseJoinColumns={#ORM\JoinColumn(unique=true, onDelete="CASCADE")}
* )
* #ORM\OrderBy({"position" = "ASC"})
*/
private $galleryItems;
Entity GalleryItem
/**
* #Assert\NotNull()
* #var Media
* #ORM\OneToOne(targetEntity="Media", cascade={"persist", "remove"}, fetch="EAGER")
* #ORM\JoinColumn(onDelete="CASCADE")
*/
private $media;
I would like when I delete a brand, the medias are also deleted.
My first problem is : When I delete the brand, the videos are deleted but not the attachments.
In addition I would like to perform operations (delete files on the server, ...) when I delete a brand.
So I tried to create an event subscriber but I do not fit in the condition.
public function postRemove(LifecycleEventArgs $args)
{
$media = $args->getEntity();
if ($media instanceof Media) {
dump("test");
exit;
}
}

Symfony Entity one-to-one relation

Can you look at the relationship between entities, unfortunately all the time is not working properly. I will be grateful for the help. This is a relation 1 to 1.
User Entity
/**
*
* #ORM\Column(type="string")
*
* #ORM\OneToOne(targetEntity="Structure", mappedBy="user")
*/
protected $structure;
Structure Entity
/**
* #var string
*
* #ORM\Column(name="user", type="string", length=50, unique=false)
* #ORM\OneToOne(targetEntity="User", inversedBy="structure")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
Try this one ...
/**
* #ORM\OneToOne(targetEntity="User",
* inversedBy="structure")
*/
private $user;
/**
*#ORM\OneToOne(targetEntity="Structure",mappedBy="user",cascade={"persist","remove"})
*/
private $structure;
As suggested by #Albeis in a comment:
Try to remove #ORM\Column of both sides...relations itself don't
have column..Regards Check
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-bidirectional
This solved my problem. The issue was about to remove #ORM\Column of both sides. Thanks for help.
You can't use mappedBy with #OneToOne. You should use inversedBy on both sides.
/**
* #ORM\OneToOne(targetEntity="Core\UserBundle\Entity\Profile", mappedBy="user")
*/
private $profile;
/**
* #ORM\OneToOne(targetEntity="Core\UserBundle\Entity\User", inversedBy="profile")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id",onDelete="CASCADE")
*/
private $user;
user_id inside profile entity. if you set onDelete="CASCADE", if you delete user, profile will deleting automatically by mysql.

Troubles with persisting relations

I ran into a problem when I'm trying to persist User entity with its relations:
class User {
/**
* #OneToMany(targetEntity="AppBundle\Entity\UserDataAttribute", mappedBy="user", cascade={"persist", "remove"})
* #var \Doctrine\Common\Collections\Collection
*/
private $customAttributes;
}
class UserDataAttribute
{
/**
* #ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="customAttributes")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
}
When I specify #JoinColumn as name="user_id" in database goes this query:
INSERT INTO user_data_attr (user_id) VALUES (null);
And when I specify #JoinColumn as name="userId" in database goes this query:
INSERT INTO user_data_attr (userId) VALUES (2323 (the actual value)));
But what I need is:
INSERT INTO user_data_attr (user_id) VALUES (2323);
What do I do wrong?
/**
* #OneToMany(targetEntity="AppBundle\Entity\UserDataAttribute", mappedBy="user", cascade={"persist", "remove"})
*/
private $customAttributes;
/**
* #ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="customAttributes")
*/
private $user;
And check it before:
app/console doctrine:schema:validate for Symfony 2
`app/console doctrine:schema:valid` for Symfony 3

Removing related entity Symfony2

I have related entities. Many to Many relation and the annotation exists on only one entity:
/**
* #ORM\ManyToMany(targetEntity="Event")
* #ORM\JoinTable(name="viewed_events",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="event_id", referencedColumnName="id")}
* )
**/
protected $viewedEvents;
The problem is when I try to delete Event entity I get Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails error. How do I solve this? I tried adding orphanRemoval=true like that: #ORM\ManyToMany(targetEntity="Event", orphanRemoval=true) and also tried adding cascade="delete" and cascade="all" instead with no success.
I give you one simple example so that you can work out what you need to add/change in your application.
Assume that there is a M-N relationship between Student and Course entities.
STUDENT
class Student
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="studentInverse", cascade={"persist", "remove"})
*/
protected $studentInverse;
public function __construct()
{
$this->studentInverse = new \Doctrine\Common\Collections\ArrayCollection();
}
}
COURSE
class Course
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="StudentCourse", mappedBy="courseMap", cascade={"persist", "remove"})
*/
protected $courseInverse;
public function __construct()
{
$this->courseInverse = new \Doctrine\Common\Collections\ArrayCollection();
}
}
STUDENTCOURSE (this the one you're more interested in)
class StudentCourse
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Course", inversedBy="courseInverse")
* #ORM\JoinColumn(name="course", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $courseMap;
/**
* #ORM\ManyToOne(targetEntity="Student", inversedBy="studentInverse")
* #ORM\JoinColumn(name="student", referencedColumnName="id", nullable=false, onDelete="CASCADE")
*/
protected $studentMap;
}
onDelete="CASCADE"
On Yours JoinColumn.

Resources