I've been trying to solve a problem since morning and I can't figure it out. I'm working on a project that uses Symfony 6 and PHP 8.0.
I have an OneToMany connection between Student and Grade -> Each Student can have many Grade objects. Each Grade relates to one Student.
But in the Grade entity, I also have a ManyToOne link between Grade and Course -> Each Grade relates to one Course. Each Course can have many Grade objects.
When I delete a student's grade (from the Student table), in the Grade table I encounter a behavior like this: The studentID is deleted, but the courseID remains. I expected the entire entry from the Grade table to be deleted.
I tried to add orphanRemove = true
on grade property from the Student table, and it's even worse. All grades for that course are deleted, including the course.
And I can understand why this happened. It's because I link the Grade both to the Student class and Course class, and I'm running into this problem:
When using the orphanRemoval=true option Doctrine makes the assumption that the entities are privately owned and will NOT be reused by other entities. If you neglect this assumption your entities will get deleted by Doctrine even if you assigned the orphaned entity to another one
I wonder how I could solve this problem. Is creating a cron job to delete all grades with student_id = null a good solution or is there a better alternative?
class Student extends AbstractUser
{
#[ORM\OneToMany(mappedBy: 'student', targetEntity: Grade::class, cascade: ['persist', 'remove'])]
protected Collection $grades;
}
class Grade {
#[ORM\ManyToOne(targetEntity: Student::class, inversedBy: 'grades')]
protected ?Student $student;
#[ORM\ManyToOne(targetEntity: Course::class, cascade: ['persist', 'remove'], inversedBy: 'grades')]
protected ?Course $course;
}
class Course {
#[ORM\OneToMany(mappedBy: 'course', targetEntity: Grade::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
protected Collection $grades;
}
I also tried to add nullable = false like:
#[
ORM\ManyToOne(targetEntity: Student::class, inversedBy: 'grades'),
ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')
]
protected ?Student $student;
#[
ORM\ManyToOne(targetEntity: Course::class, inversedBy: 'grades'),
ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')
]
protected ?Course $course;
and I am struck by the logical error An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'student_id' cannot be null
I would like to specify that I use EasyAdmin 4.0 to do this. In StudentCrudController my grades property looks like this:
CollectionField::new('grades')
->setEntryType(GradeType::class)
->setEntryIsComplex(),
CodePudding user response:
Remove cascade
parameter from Grade::course
, because if you delete Grade, the Course and Grades belonging to it will be deleted
class Student extends AbstractUser
{
#[ORM\OneToMany(mappedBy: 'student', targetEntity: Grade::class, cascade: ['persist', 'remove'])]
protected Collection $grades;
}
class Grade {
#[
ORM\ManyToOne(targetEntity: Student::class, inversedBy: 'grades'),
ORM\JoinColumn(nullable: false, onDelete: 'CASCADE'),
]
protected ?Student $student;
#[
ORM\ManyToOne(targetEntity: Course::class, inversedBy: 'grades'),
ORM\JoinColumn(nullable: false, onDelete: 'CASCADE'),
]
protected ?Course $course;
}
Also you can add orphanRemove = true
to Student::grades