I want to delete a property with my user but I have the following error :
An exception occurred while executing 'DELETE FROM property WHERE id = ?' with params [1]:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (gestImmo.equipment, CONSTRAINT FK_D338D583517FE9FE FOREIGN KEY (equipment_id) REFERENCES property (id))
I googled it and (I think) it's a cascade problem. So I searched on forums but I didn't solved the problem. I asked for help to an experimented coworker but we didn't fix the mistake ... Hope you could help me.
In my User entity there is :
/**
* #ORM\OneToMany(targetEntity="App\Entity\Property", mappedBy="userProperty")
*/
private $properties;
In my property Entity there is :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="properties")
* #JoinColumn(name="id", referencedColumnName="id", onDelete="CASCADE")
*/
private $userProperty;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Equipment", mappedBy="equipment")
* #JoinColumn(name="id", referencedColumnName="equipement_id", onDelete="CASCADE")
*/
private $equipments;
and in my equipments entity there is :
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Property", inversedBy="equipments")
* #Assert\Type("string")
* #var string
*/
private $equipment;
Thanks for your help !
Setting the onDelete="CASCADE" in your manyToOne annotation should be enough.
Don't forget to bin/console make:migration after that. Everything you change in your ORM must be migrated in order to apply the changes into the database.
You cannot delete property because it is referenced in column equipment_id in table equipment. This foreign key constraint means that equipment.equipment_id must point to a valid(existing) id of property - property.id.
If you want to delete property, then before that you either:
Delete equipment records where equipment_id = {id of property you want to delete}
Or change those equipment_id to null
EDIT
Looks like your annotations are incorrect. If I understood your relations correctly, then it should be something like this.
Property entity:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="properties")
* #JoinColumn(name="user_property_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $userProperty;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Equipment", mappedBy="equipment")
*/
private $equipments;
Equipment entity:
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Property", inversedBy="equipments")
* #ORM\JoinColumn(name="equipment_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $equipment;
Related
i have two entities : Post and Comments where a Post can have one or more comments.
I implemented a cascade={"remove"} on the Post's comments OneToMany which works greats !
All comments related to a Posts gets deleted after Post suppression.
Then, i decided to add a topComment attribute in my Post Entity (Many topComment to One Post). After that, i tried to delete a Post with related Comments but the suppression fails because of the foreign key constraint even if the post have no topComment...
Here is the error i get :
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`lab_test_cascade_remove`.`comment`, CONSTRAINT `FK_9474526C4B89032C` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`))
I got rid of this error by adding a #JoinColumn(nullable=true) annotation to the Post's topComment attribute but i find that strange because this is the default behavior of doctrine if #JoinColumn annotation isn't set...
Could someone can please explain me what happens internally and cause this error even if the Post don't have any topComment ?
Many thanks in advance.
Here are my entities (attributes only) :
Post :
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
*/
class Post
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="post", cascade={"remove"})
*/
private $comments;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Comment")
*/
private $topComment;
Comment :
/**
* #ORM\Entity(repositoryClass="App\Repository\CommentRepository")
*/
class Comment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="comments")
* #ORM\JoinColumn(nullable=false)
*/
private $post;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
I have made a really basic example with two models.
"Singer" extends from "Person"
I am using class table Inheritance with these two models:
<?php
namespace models;
/**
* #Table(name="persons")
* #entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="type", type="string")
* #DiscriminatorMap({"singer" = "\models\Singer"})
*/
abstract class Person {
/**
* #Id
* #Column(type="integer", nullable=false, name="id_person")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #column(type="string", nullable=false) */
protected $name;
The singer model looks like:
namespace models;
/**
* #Table(name="singers")
* #entity
*/
class Singer extends Person{
/** #column(type="string", nullable=false) */
protected $gender;
}
Workflow
Consider this scenario.
In the db I have these rows:
persons table:
id_person | name | type
-----------------------
1 john singer
singers table:
id_person | gender
------------------
1 pop
I proceed to remove this singer:
$singer = $em->find('models\Singer', 1);
$em->remove($singer);
$em->flush();
After execute the code above, I check again the database and I found this:
persons table:
id_person | name | type
-----------------------
(empty)
singers table:
id_person | gender
------------------
1 pop
As you note, the row from child table was not removed as expected.
So, after searching in doctrine's documentation, it states:
When you do not use the SchemaTool to generate the required SQL you should know that deleting a class table inheritance makes use of the foreign key property ON DELETE CASCADE in all database implementations. A failure to implement this yourself will lead to dead rows in the database.
So, I proceed to alter persons table as below:
ALTER TABLE persons
ADD CONSTRAINT fk_persons_1
FOREIGN KEY (id_person)
REFERENCES singers (id_person)
ON DELETE CASCADE
ON UPDATE NO ACTION;
Now, the problems get complicated:
when I remove a singer, the information still there, even the persons table was altered in order to delete from singers table too.
When I try to insert a new singer like
$singer = new \models\Singer('Zara', 'rock');
$em->persist($singer);
$em->flush();
it throws an exception:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`practica`.`persons`, CONSTRAINT `fk_persons_1` FOREIGN KEY (`id_person`) REFERENCES `singers` (`id_person`) ON DELETE CASCADE ON UPDATE NO ACTION)' in /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:138 Stack trace: #0 /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php(138): PDOStatement->execute(NULL) #1 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php(165): Doctrine\DBAL\Statement->execute() #2 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(929): Doctrine\ORM\Persisters\JoinedSubclassPersister->executeInserts() #3 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(318): Doctrine\ORM\UnitOfWork->executeInserts(Object(Doctrine\ORM\Mapping\ClassMetadata)) #4 /var/www/html/doctrine/vendor/doct in /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php on line 47
So, basically, all I need is to remove the information in child table too in MySQL db. But I do not get it.
Try reversing the FOREIGN KEY i.e. remove it from your persons table and add it to your singers table
ALTER TABLE singers
ADD CONSTRAINT fk_singers_1
FOREIGN KEY (id_person)
REFERENCES persons (id_person)
ON DELETE CASCADE
ON UPDATE NO ACTION;
I think you probably have to ask yourself if you really need a field relating to multiple tables.
In any case, maybe is better to do it through a relation instead of using inheritance and a discriminator map, that works well with objects using the same table, but since in your case they are different tables it is probably better to use a relation instead:
<?php
namespace models;
/**
* #Table(name="persons")
* #entity
*/
class Person {
/**
* #Id
* #Column(type="integer", nullable=false, name="id_person")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #column(type="string", nullable=false) */
protected $name;
/**
* #OneToOne(targetEntity="Singer")
* #JoinColumn(name="id_person", referencedColumnName="id_person")
*/
private $singer;
namespace models;
/**
* #Table(name="singers")
* #entity
*/
class Singer{
/**
* #Id
* #Column(type="integer", nullable=false, name="id_person")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #column(type="string", nullable=false) */
protected $gender;
/**
* #OneToOne(targetEntity="Person", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
* #JoinColumn(name="id_person", referencedColumnName="id_person")
*/
private $person;
}
By trying to attach the design to the real world you are over complicating the implementation which will make your application hard to mantain.
I did not try the code (probably needs adjustement).
I am trying to construct an object with two composite foreign keys pointing out to the same object, but they seem to have the same data, like doing the join only on one column, product_id.
class PostpaidProduct extends Product {
/**
* #ManyToOne(targetEntity="Bundle", fetch="EAGER", cascade={"persist"})
* #JoinColumn(name="bundle_voice_id", referencedColumnName="id")
*/
private $bundleVoice;
/**
* #ManyToOne(targetEntity="Bundle", fetch="EAGER", cascade={"persist"})
* #JoinColumn(name="bundle_data_id", referencedColumnName="id")
*/
private $bundleData;
/**
* #OneToMany(targetEntity="BundlePromo", mappedBy="product", fetch="EAGER", cascade={"persist"})
* #JoinColumns({
* #JoinColumn(name="id", referencedColumnName="product_id"),
* #JoinColumn(name="bundle_voice_id", referencedColumnName="bundle_id")
* })
*/
private $bundleVoicePromos;
/**
* #OneToMany(targetEntity="BundlePromo", mappedBy="product", fetch="EAGER", cascade={"persist"})
* #JoinColumns({
* #JoinColumn(name="id", referencedColumnName="product_id"),
* #JoinColumn(name="bundle_data_id", referencedColumnName="bundle_id")
* })
*/
private $bundleDataPromos;
}
What would be wrong with my mapping?
Is it possible to have composite foreign keys but without being primary keys?
I have talked to one of the developers of Doctrine and he said that the #JoinColumns field in #OneToMany relationships is ignored. The alternative would be having just one foreign key and to use matching criterias in an entity method, filtering for the entries needed based on the other key. Another solution would be having repository methods specific for getting these values.
Also, in OneToMany relationships eager fetching does not work, so it does separate queries for all children. So if you have a product with multiple prices, when fetching a product it will do separate queries for fetching the prices.
I have the following entity with this rekation:
/**
* Acme\DemoBundle\Entity\Book
*
* #ORM\Table(name="book")
* #ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\BookRepository")
* #ORM\HasLifecycleCallbacks
* #UniqueEntity(fields="publickey", groups={"publickey"})
*/
class P1guestlistentry {
/**
* #var P1guestlistentrystatistic
*
* #ORM\OneToOne(targetEntity="P1guestlistentrystatistic", orphanRemoval=true, cascade={"all"}, fetch="EAGER")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="fkstatistic", referencedColumnName="pkId", nullable=false)
* })
*/
private $fkstatistic;
When I try to remove an object like here:
$this->getEntityManager()->getConnection()->beginTransaction();
try{
$book = $this->getEntityManager()->getRepository('AchmeDemoBundle:Book')->find(3928);
$this->getEntityManager()->remove($book);
$this->getEntityManager()->flush();
$this->getEntityManager()->getConnection()->commit();
}catch(Exception $e){
$this->getEntityManager()->getConnection()->rollBack();
echo $e->getMessage();
}
exit;
I can do whatever I want, I get the following error:
An exception occurred while executing 'DELETE FROM book WHERE pkId = ?' with params {"1":3928}: SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (p1.book, CONSTRAINT FK_F51A442F78734022 FOREIGN KEY (fkstatistic) REFERENCES bookstatistic (pkId))
Has anbybody an idea what I'm doing wrong?
I tried a lot of methods but nothing helps.
In case someone runs into a similar issue, here is the solution:
/**
* #var statistic
*
* #ORM\OneToOne(targetEntity="statistic", cascade="ALL")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="fkStatistic", referencedColumnName="pkId", onDelete="SET NULL")
* })
*/
The onDelete option will remove the relation first and then doctrine will do the cascade operation.
I have a reference table album_content which has: album_id, content_id, and sort_key. I set it up as an entity with #ManyToOne relations:
/**
* #ManyToOne(targetEntity="Album")
* #JoinColumns({
* #JoinColumn(name="album_id", referencedColumnName="id")
* })
*/
private $albumId;
/**
* #ManyToOne(targetEntity="Content")
* #JoinColumns({
* #JoinColumn(name="content_id", referencedColumnName="id")
* })
*/
private $contentId;
/**
* #Column(name="sort_key", type="integer", nullable=false)
*/
private $sortKey;
Right now Doctrine is complaining No identifier/primary key specified. What's the correct annotation to reference these without adding an extra ID column?
First, you probably shouldn't be naming things $contentId or $albumId, but instead just call them $content and $album.
That said, the quick solution is to add #Id annotations to both of your associations.
The manual goes into further detail about using composite keys in Doctrine 2.