Symfony3 - Delete Entity with OneToMany - relationship in Doctrine - cascade not working - symfony

I have two entities:
/**
* Course
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\Repository\CourseRepository")
*/
class Course
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection()
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\TimeTableEntry", mappedBy="course", cascade={"remove"}, orphanRemoval=true)
*
*/
private $timeTableEntries;
/**
* #var boolean
*
* #ORM\Column(name="enabled", type="boolean", nullable=true)
*/
private $enabled;
and
/**
* TimeTableEntry
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\Repository\TimeTableEntryRepository")
*/
class TimeTableEntry
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\ArrayCollection()
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Course", inversedBy="timeTableEntries", cascade={"ALL"})
* #ORM\JoinColumn(name="course_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $course;
As you can see I tried with cascade={"ALL"}, onDelete="CASCADE" and orphanRemoval=true.
When I want to delete the Course Entity I get the error message:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
update a parent row: a foreign key constraint fails
(edutalk.teaching_event, CONSTRAINT FK_F2B1088B57042871 FOREIGN
KEY (time_table_entry_id) REFERENCES time_table_entry (id))
What can I try to resolve this?

I cannot comment on your post, so I post it as an answer.
Your problem is that you have another entity TeachingEvent with a reference to TimeTableEntry. When you try to delete a TimeTableEntry, it is impossible because this time_table_entry_id is still referenced as a foreign key in the TeachingEvent table.
Before deleting the TimeTableEntry, first you should unlink it from the TeachingEvent. I don't have your code, but depending on how TimeTableEntry and TeachingEvent relate to each other, it's probably something like:
$teachingEvent->setTimeTableEntry(null);
or
$timeTableEntry->setTeachingEvent(null);

Related

can a doctrine entity have multiple one to one relationships where from and to fields are similar?

class Order{
/**
* #var integer
*
* #ORM\Column(name="order_id", type="bigint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $orderId;
/**
* #ORM\OneToOne(targetEntity="OrderDiscount")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
*/
private $discount;
/**
* #ORM\OneToOne(targetEntity="OrderTax")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
*/
private $tax;
/**
* #ORM\OneToOne(targetEntity="OrderPayments")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
*/
private $payment;
}
class OrderDiscount{
/**
* #var integer
*
* #ORM\Column(name="order_id", type="bigint")
* #ORM\Id
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="discount", type="float", precision=10, scale=0, nullable=false)
*/
private $discount;
}
class OrderPayments{
/**
* #var integer
*
* #ORM\Column(name="order_id", type="bigint", nullable=false)
* #ORM\Id
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="payment_amount", type="float", precision=10, scale=0, nullable=true)
*/
private $paymentAmount;
}
class OrderTax{
/**
* #var integer
*
* #ORM\Column(name="order_id", type="bigint", nullable=false)
* #ORM\Id
*/
private $id;
/**
* #var float
*
* #ORM\Column(name="tax_amount", type="float", precision=10, scale=0, nullable=false)
*/
private $taxAmount;
}
Here is my code (showing just basic code here).
when i migrate this schema doctrine says that an index is already exists, when i check in database that index do not exist.
Because doctrine in using column names to generate indexes, because all associations have same columns thats why doctrine says index already exists.
Is there any other better way to do this?
Yes, there is a better way to do this.
What you are trying to do, simply does not work because doctrine tries to create a foreign key from the "order_id" column from Order entity `s table that point to each of the "order_id" columns of Discount, Tax, Payments entities' tables.
What you actually want is to make the primary key order_id from Order to be a foreign key and also a primary key on Discount, Tax and Payments tables.
To achieve this:
Make the relation Bidirectional
Define the owning side on the Discount, Tax and Payments entities.
Here is the code:
class Order{
/**
* #var integer
*
* #ORM\Column(name="order_id", type="bigint")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $orderId;
/**
* #ORM\OneToOne(targetEntity="OrderDiscount", mappedBy="orderId")
*/
private $discount;
/**
* #ORM\OneToOne(targetEntity="OrderTax")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
*/
private $tax;
}
class OrderDiscount{
/**
* #var integer
*
* #ORM\OneToOne(targetEntity="Order", inversedBy="discount")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
* #ORM\Id
*/
private $orderId;
/**
* #var float
*
* #ORM\Column(name="discount", type="float", precision=10, scale=0, nullable=false)
*/
private $discount;
}
class OrderTax{
/**
* #var integer
*
* #ORM\OneToOne(targetEntity="Order", inversedBy="tax")
* #ORM\JoinColumn(name="order_id", referencedColumnName="order_id")
* #ORM\Id
*/
private $orderId;
/**
* #var float
*
* #ORM\Column(name="tax_amount", type="float", precision=10, scale=0, nullable=false)
*/
private $taxAmount;
}
Note
that ORM\JoinColumn is used on the owning side, so you do not need this your Order entity relation fields definition.
You do not need #ORM\Column(name="order_id", type="bigint", nullable=false) on Discount, Tax, Payments as it is defined as a foreign key and it will be created as the same type as the column it "points" to(in this case order_id of Order).

Doctrine2 relation for non Id column

I'm building a simple web-service using Symfony 3, Doctrine 2.5 and stuck at ORM relations described below in simplified structure.
I have an Action entity containing many actions with ManyToOne relation...
class Action
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\ManyToOne(targetEntity="\AppBundle\Entity\Status")
* #ORM\JoinColumn(referencedColumnName="code", nullable=false)
*/
private $status;
and the Status Entity with a few statuses.
class Status
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="integer", unique=true)
*/
private $code;
I cannot get proper way to set referencedColumnName="code" column (not 'Id' as usual) for Action entity.
Configured this way repo throws wxception at persist moment with "Notice: Undefined index: code";
I guess that it is mappedBy or inversedBy annotation parameter... but can't figure out "how".
Unfortunately it's not supported in Doctrine (reference).
You may edit your Status entity like this (ensure that code is set before persist):
class Status
{
/**
* #ORM\Column(name="code", type="integer", unique=true)
* #ORM\Id
*/
private $code;
}
If autoincremented field is your requirement you can take a look on this answer for possible solutions.
Just thought I'd add you can still use the non-primary keys as many to many, by using the entity itself as the join table. This will work but you still need to set your relationship keys correctly.
Example:
/**
* #ORM\Entity
*/
class Car {
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(name="registration_code", type="text", length=128, nullable=false)
* #var string
*/
public $registrationCode;
/**
* #var \Doctrine\Common\Collections\Collection
* #ORM\ManyToMany(targetEntity="Registration", mappedBy="Cars")
* #ORM\JoinTable(name="car",
* joinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="registration_code", referencedColumnName="registration_code")}
* )
*/
public $Registrations;
public function __construct() {
$this->Cars = new ArrayCollection();
}
}
/**
* #ORM\Entity
*/
class Registration {
/**
* #var integer
*
* #ORM\Column(name="id", type="bigint", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #ORM\Column(name="registration_code", type="text", length=128, nullable=false)
* #var string
*/
public $registrationCode;
/**
* #var ArrayCollection
* #ORM\ManyToMany(targetEntity="Car", mappedBy="Registrations")
* #ORM\JoinTable(name="car",
* joinColumns={#ORM\JoinColumn(name="registration_code", referencedColumnName="registration_code")},
* inverseJoinColumns={#ORM\JoinColumn(name="id", referencedColumnName="id")}
* )
*/
public $Cars;
public function __construct() {
$this->Cars = new ArrayCollection();
}
}
The upside is that it works fine as a workaround.
Keep in mind a few things:
it's a collection not a single instance;
column has to be managed manually on your end;
you must set up constraints correctly (indexes, keys, etc);
check your queries still perform!

Doctrine - "Missing value for primary key"

I have a problem with an invalid mapping. I keep getting an error with message:
Missing value for primary key course on "Namespace\XXX\Entity\Subject"
The doctrine:schema:validate command returns the following:
[Mapping] FAIL - The entity-class 'Namespace\XXX\Entity\Subject' mapping is invalid:
* The join columns of the association 'schedule' have to match to ALL identifier columns of the target entity 'Namespace\XXX\Entity\Subject', however 'id, course, class, day, timeslot' are missing.
This is my mapping:
Subject-entity
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="message", type="string", length=255)
*/
private $message;
/**
* #ORM\ManyToOne(targetEntity="Oggi\ScheduleBundle\Entity\Schedule", inversedBy="subjects")
* #ORM\JoinColumn(name="schedule", referencedColumnName="id")
*/
private $schedule;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date")
*/
private $date;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
Schedule Entity
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
*/
private $id;
/**
* #var string
*
* #ORM\Id
* #ORM\OneToOne(targetEntity="Oggi\KlasBundle\Entity\Course")
* #ORM\JoinColumn(name="course", referencedColumnName="id")
*/
private $course;
/**
* #var string
*
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Oggi\KlasBundle\Entity\Klas", inversedBy="schedules")
* #ORM\JoinColumn(name="class", referencedColumnName="id")
*/
private $klas;
/**
* #var string
*
* #ORM\Id
* #ORM\OneToOne(targetEntity="Day")
* #ORM\JoinColumn(name="day", referencedColumnName="id")
*/
private $day;
/**
* #var string
*
* #ORM\Id
* #ORM\OneToOne(targetEntity="Timeslot")
* #ORM\JoinColumn(name="timeslot", referencedColumnName="id")
*/
private $timeslot;
/**
* #ORM\OneToMany(targetEntity="Oggi\CalendarBundle\Entity\Subject", mappedBy="schedule")
*/
private $subjects;
I suppose that what Symfony is telling me to do is include all primary keys in my subject-entity. Is there a way to only include the ID of the schedule in this entity. I had this in mind:
Thanks in advance!
The reason you get this error is because all columns in your Schedule entity are marked as an Id.
Just remove #ORM\Id from all columns but $id - this should fix the case

One join table for two Many-to-Many relations

I'm working on a solution for adding tags to two differente entities.
in order to get data easily in the frontend i created a joinTable named Tag_Mapping like this :
class TagMapping
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Tag", inversedBy="tags")
*/
private $tag;
/**
* #ORM\ManyToOne(targetEntity="Feed", inversedBy="tags")
*/
private $feed;
/**
* #ORM\ManyToOne(targetEntity="Question", inversedBy="tags")
*/
private $question;
...
}
The Tag Entity :
class Tag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255, unique=true)
*/
private $name;
/**
* #Gedmo\Slug(fields={"name"})
* #ORM\Column(unique=true)
*/
private $slug;
/**
* #ORM\OneToMany(targetEntity="TagMapping", mappedBy="tag", cascade="remove")
*/
private $tags;
...
}
and in both other entities (Feed and Question) I made reference to TagMapping entity like this
...
/**
* #ORM\ManyToMany(targetEntity="Tag")
* #JoinTable(name="tag_mapping",
* joinColumns={#JoinColumn(name="feed_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
private $tags;
...
the problem I'm facing is that it's not a valid way to do it, as it shows an error when i'm trying to execute :
php app/console doctrine:schema:update --force
saying that tag_mapping table already exists.
do you have any idea how can i get it done using only one joinTable instead of one for each relation ?
Thanks.

Doctrine 2: OneToMany Relation, order by foreign key

I have three entities (Profile, ProfileValue and Value).
A Profile has a one-to-many relation with ProfileValue, which has a many-to-one relation with the Value entity.
Is it possible to get the rleated ProfileValues from the Profile, ordered by the value id?
If I add the orderby annotation for a non-foreign-key like the enabled field in ProfileValue, it works. But for a foreign key, it failed with the message "inrecognized field". Any ideas?
/**
*
* #ORM\Table(name="profile")
* #ORM\Entity
*/
class Profile {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var array $profileCValues
* #ORM\OneToMany(targetEntity="ABC\XYZBundle\Entity\ProfileValue", mappedBy="profile", cascade={"persist"})
* #ORM\OrderBy({"value" = "ASC"})
*/
private $profileValues;
And here is the ProfileValue entity:
/**
* ABC\XYZBundle\Entity\ProfileValue
*
* #ORM\Table(name="profile_value", indexes={#ORM\Index(columns={"profile_id"}), #ORM\Index(columns={"value_id"}) })
* #ORM\Entity
*/
class ProfileValue {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var integer $enabled
*
* #ORM\Column(name="enabled", type="boolean", length=1, nullable=true)
*/
private $enabled;
/**
* #var ABC\XYZBundle\Entity\Profile
* #ORM\ManyToOne(targetEntity="ABC\XYZBundle\Entity\Profile", inversedBy="profileValues")
* #ORM\JoinColumn(name="profile_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $profile;
/**
* #var ABC\XYZBundle\Entity\Value
* #ORM\ManyToOne(targetEntity="ABC\XYZBundle\Entity\Value")
* #ORM\JoinColumn(name="value_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $value;
}
Had the same problem and solved it by adding a new field with foreign key:
/**
* #var integer $valueId
*
* #ORM\Column(name="value_id", type="integer")
*/
private $valueId;
Then you can order it without problems:
/**
* #var array $profileCValues
* #ORM\OneToMany(targetEntity="ABC\XYZBundle\Entity\ProfileValue", mappedBy="profile", cascade={"persist"})
* #ORM\OrderBy({"valueId" = "ASC"})
*/
private $profileValues;
Does this work?
/**
* #var array $profileCValues
* #ORM\OneToMany(targetEntity="ABC\XYZBundle\Entity\ProfileValue", mappedBy="profile", cascade={"persist"})
* #ORM\OrderBy({"id" = "ASC"})
*/
private $profileValues;
I don't use XML but this works in YML:
oneToMany:
foos:
targetEntity: Company\ProjectBundle\Entity\Foo
mappedBy: bar
orderBy: { 'id': ASC }

Resources