I have a series of classes with a slightly complicated set of references between the properties of those classes. I am trying to remove an entity and have that remove be cascaded to its children, but I'm running into foreign key constraint errors. Here is an example of my class structure:
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_tests")
*/
class StudentTest implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="studentTest", cascade{"remove","persist"})
*/
protected $studentTestItems;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="studentTests")
*/
protected $test;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $created;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTests")
*/
protected $student;
}
//...
<?php
/**
* #ORM\Entity
* #ORM\Table(name="student_test_items")
*/
class StudentTestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
/**
* #var User
* #ORM\ManyToOne(targetEntity="User", inversedBy="studentTestItems", cascade={"persist"})
*/
protected $student;
/**
* #var TestItem
* #ORM\ManyToOne(targetEntity="TestItem", inversedBy="studentTestItems", cascade{"persist"})
*/
protected $testItem;
}
//...
/**
*
* #ORM\Table(name="tests")
* #ORM\Entity
*
* #ORM\HasLifecycleCallbacks
*/
class Test implements IEntityAccess {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var \DateTime
* #ORM\Column(type="datetime", nullable=true)
*/
protected $startDate;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="test" )
*/
protected $studentTests;
/**
* #var TestItem[]
* #ORM\OneToMany(targetEntity="TestItem", mappedBy="test", cascade={"all"})
*/
protected $items;
}
//...
/**
*
* #ORM\Table(name="test_items")
* #ORM\Entity
*/
abstract class TestItem {
/**
*
* #var int
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var Test
* #ORM\ManyToOne(targetEntity="Test", inversedBy="items")
*/
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="testItem")
*/
protected $studentTestItems;
}
/**
* This is the primary user object. Used for login and all the other
* good stuff.
*
* #ORM\Table(name="users")
* #ORM\HasLifecycleCallbacks
class User implements AdvancedUserInterface, \Serializable, IEntityAccess
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $id;
/**
* #var StudentTest[]
* #ORM\OneToMany(targetEntity="StudentTest", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTests;
/**
* #var StudentTestItem[]
* #ORM\OneToMany(targetEntity="StudentTestItem", mappedBy="student", cascade={"persist", "remove"})
*/
protected $studentTestItems;
}
Let's say I want to delete a student test, and have that delete cascaded to its StudentTestItem children. To do so, I run the following code inside of a controller.
//... blah blah class definition
/**
* Delete a student test
*
* #return \Symfony\Component\HttpFoundation\Response
* #Route("/studenttest/delete", name="student_test_delete")
*/
public function DeleteStudentTestAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$studentTest = $em->getRepository("MyAcmeBundle:StudentTest")->findOneBy(array("id" => 3));
$em->remove($studentTest);
$em->flush();
return $this->redirect($this->generateUrl('student_delete_success'));
}
When I try to run that code, I get the following error message:
An exception occurred while executing 'DELETE FROM student_tests WHERE id = ?' with params [3]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`my_acme_bundle/student_test_items`, CONSTRAINT `FK_71FA2A7F36BB1A1` FOREIGN KEY (`student_test_id`) REFERENCES `student_tests` (`id`))
500 Internal Server Error - DBALException
NOW, if I remove all references to studentTestItems from the classes, i.e. I comment out $studentTestItems from the TestItem and User classes, it deletes fine without that issue. Why is this happening? Does Doctrine keep track of the parent references through associations or something?
Looks like you forgot to add ON DELETE CASCADE to the foreign key constraint. Try changing the following association in class StudentTestItem:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
*/
protected $studentTest;
To this:
/**
* #var StudentTest
* #ORM\ManyToOne(targetEntity="StudentTest", inversedBy="studentTestItems")
* #ORM\JoinColumn(name="student_test_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $studentTest;
Related
Is it possible to keep an array of entities A in the entity A ? How to do this with Doctrine ?
I have :
class A {
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*/
private $sisters;
}
But I don't know what to add to have Doctrine do what I need.
A can have many sisters, and many sisters can be sister of A (Many-To-Many, Self-referencing):
/**
* #ORM\Entity()
* #ORM\Table()
*/
class A
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\A")
*/
private $sisters;
}
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-self-referencing
Trying to utilize inheritance, I've created the following entities:
/**
* #ORM\Table(name="persons")
* #ORM\Entity()
*/
class Person
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name;
/**
* #ORM\OneToOne(targetEntity="Image", cascade={"persist"})
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
protected $image;
}
/**
* #ORM\Table(name="actors")
* #ORM\Entity()
*/
class Actor extends Person
{
/**
* #ORM\Column(name="character", type="string", length=255)
*/
private $character;
}
/**
* #ORM\Table(name="images")
* #ORM\Entity()
*/
class Image
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
}
Which almost works perfectly. The generated actors-table contains all the persons-fields, except for the image-relation. I've tried to change the relation to a ManyToOne, which didn't help.
How to make the Actor-entity also inherit all joined fields? I'm open to other solutions, if the above isn't ideal.
You need a parent construct in your Actor class:
public function __construct()
{
parent::__construct();
// your own logic
}
It is advised that you add an ID aswell.
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.
There are 2 entity many-to-many relations with join table in symfony 2. First one is certificate, second one is mayag and join table is certificate_mayag. Certificate has many mayag with isAvailable, startDate, endDate fields. A mayag has many certificate.
I want to render this relation by checkbox form. The form consists of certificate info and mayag list with checkbox and startdate, enddate and isavailable fields. How to do this solution and which one is the best way to develop?
<?php
/**
* Created by PhpStorm.
* User: Mendbayar
* Date: 12/8/13
* Time: 1:00 PM
*/
namespace Mnd\SrdBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Mnd\SrdBundle\Repository\CertifyRepository")
* #ORM\Table(name="Certify")
*/
class Certify {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", unique=true, length=50)
*/
protected $certificate_number;
/**
* #ORM\ManyToOne(targetEntity="CertificateOwner", inversedBy="certificates")
* #ORM\JoinColumn(name="owner_id", referencedColumnName="id")
*/
protected $owner;
/**
* #ORM\ManyToOne(targetEntity="Document", inversedBy="certificates")
* #ORM\JoinColumn(name="document_id", referencedColumnName="id")
*/
protected $document;
/**
* #ORM\ManyToOne(targetEntity="Action", inversedBy="certificates")
* #ORM\JoinColumn(name="action_id", referencedColumnName="id")
*/
protected $action;
/**
* #ORM\OneToMany(targetEntity="Extension", mappedBy="certify")
*/
protected $extensions;
/**
* #ORM\OneToMany(targetEntity="CertifyMayag", mappedBy="certify")
*/
protected $mayags;
}
<?php
/**
* Created by PhpStorm.
* User: Mendbayar
* Date: 12/22/13
* Time: 1:41 PM
*/
namespace Mnd\SrdBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Mnd\SrdBundle\Repository\CertifyMayagRepository")
* #ORM\Table(name="certify_mayag")
*/
class CertifyMayag {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Certify", inversedBy="certifyMayags")
* #ORM\JoinColumn(name="certifyId", referencedColumnName="id")
*/
protected $certify;
/**
* #ORM\ManyToOne(targetEntity="Mayag", inversedBy="MayagCertifies")
* #ORM\JoinColumn(name="mayagId", referencedColumnName="id")
*/
protected $mayag;
/**
* #ORM\Column(type="boolean")
*/
protected $isAvailable;
/**
* #ORM\Column(type="datetime")
*/
protected $startDate;
/**
* #ORM\Column(type="datetime")
*/
protected $endDate;
}
<?php
/**
* Created by PhpStorm.
* User: Mendbayar
* Date: 12/22/13
* Time: 1:38 PM
*/
namespace Mnd\SrdBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="Mnd\SrdBundle\Repository\MayagRepository")
* #ORM\Table(name="Mayag")
*/
class Mayag {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
* */
protected $name;
/**
* #ORM\OneToMany(targetEntity="CertifyMayag", mappedBy="mayag")
*/
protected $certifies;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
protected $isAvailable;
/**
* #ORM\Column(type="datetime")
*/
protected $startDate;
/**
* #ORM\Column(type="datetime")
*/
protected $endDate;
/**
* #ORM\Column(type="text")
* */
protected $description;
}
3 entities have automatic getter setter.
I have a Entity of TeamMembers. And the TeamMember can have a Specification with a value.
So I have three Entities: TeamMember, Specifications, SpecificationValues.
In the SpecificationValue table I want to store the TeamMember_id, the Specification_id and the value that is just for that TeamMember.
The Specifications and TeamMembers Entities are working. But now I want to show all the Specifications, if I go to the edit route (see code example) of a TeamMember, and have to possibility over there to fill in some values that I want to store in the SpecificationValue Entity.
[TeamMember > Specifications]: list of all specifications, with a extra input field where I can insert some values, that will be stored in the SpecificationValues entity.
<?php
namespace My\BundleName\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* SpecificationValue
*
* #ORM\Table()
* #ORM\Entity
*/
class SpecificationValue
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Specifications")
* #ORM\JoinColumn(name="specification_id", referencedColumnName="id")
*/
protected $specification;
/**
* #ORM\ManyToOne(targetEntity="TeamMembers")
* #ORM\JoinColumn(name="teammember_id", referencedColumnName="id")
*/
protected $teammember;
/**
* #var string
* #ORM\Column(name="value", type="string", length=222)
*/
protected $value;
}
/**
* Specifications
*
* #ORM\Table()
* #ORM\Entity
*/
class Specifications
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
* #Gedmo\Translatable
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="SpecificationCategory")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}
/**
* TeamMembers
*
* #ORM\Table()
* #ORM\Entity
*/
class TeamMembers
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=250)
*/
protected $name;
/**
* #var boolean
*
* #ORM\Column(name="active", type="boolean")
*/
protected $active = true;
}
And the Forms are generated with generate:crud.
this is how the form should look like > http://i.stack.imgur.com/Nkkdy.png
But is that even possible with Entities in Symfony?