doctrine 2 , deleting row , contraint fails - doctrine-orm

i tried deleting from a user , inside the users entity, i have a one to many property:
/** #OneToMany(targetEntity="\Entities\comments", mappedBy="comments", cascade={"persist"}) */
protected $usercomments;
as there are comments, i cannot remove the main user... errors:
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or
update a parent row: a foreign key constraint fails
i also tried: cascade={"persist", "remove"} , still no joy when deleting...
do i have to delete all comments for this user first in a loop then remove user as i thought it would delete all related automatically.

Try * #joinColumn(onDelete="SET NULL", onUpdate="SET NULL")

Related

Trigger auto generation of field on new Entity (Doctrine in Symfony) / avoid Integrity constraint violation field cannot be null

Working with Symfony 3.x and Doctrine I have this problem:
Entity "Foo" is defined as follows:
/**
* #ORM\Entity
* #ORM\Table(name="foo")
*/
class Foo
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $incr_int;
...
}
Leaving aside that $id and $incr_int will (allways?) be the same value I get the following error when creating a new Entity of type Foo:
An exception occurred while executing 'INSERT INTO foo (incr_int) VALUES (?)' with params [null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'incr_int' cannot be null
While this seem to make sense looking at the error itself I dont get how to fix it when creating Foo like this (which I believe is the standard way in Symfony?):
$foo = new Foo();
$em->persist($foo); // $em is the entity manager
$em->flush();
Like I would expect it works if I delete the $incr_int field from Foo because the only remaining field $id is auto generated by increasing the last inserted id-value by 1. I was assuming this behaviour should be the same for the $incr_int field. Well... obviously it's not and I can't figure out why. Any help is highly appreciated.
There were 2 problems in my code (thanks to #nospor).
A table can only have one auto increment field (and a second one doesn't make much sense anyways).
When creating a new entity the id is generated when really updating the DB (flushing the entity manager).
I could solve this issue by removing the second auto increment condition and creating the Entity with the required fields (here only the auto generated id field) and then flushing it. After flushing I am able to retrieve the id and generate a second field's value holding a calculated value that depends on the id value as well.

Doctrine ManyToMany: removing an object

I have got problem with unidirectional ManyToMany relationship in Doctrine. The case is very easy: Product has many Tags. Tag can be attached to Product but also to any "taggable" entity in my model. Here is snippet of my code:
/**
* #Entity
* #Table(name="products")
**/
class Product {
/** some other fields here */
/**
* #ManyToMany(targetEntity="Tag")
* #JoinTable(name="products_tags",
* joinColumns={#JoinColumn(name="product_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="tag_id", referencedColumnName="id")}
* )
*/
protected $tags;
}
Since its unidirectional relation code of Tag class is omitted.
For such defined association Doctrine generated the following SQL code (SQL for products table and tags table is skipped):
CREATE TABLE `products_tags` (
`product_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL,
PRIMARY KEY (`product_id`,`tag_id`),
KEY `IDX_E3AB5A2C4584665A` (`product_id`),
KEY `IDX_E3AB5A2CBAD26311` (`tag_id`),
CONSTRAINT `FK_E3AB5A2CBAD26311` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`),
CONSTRAINT `FK_E3AB5A2C4584665A` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
I would like to remove product that has some tags attached to it.
/* $product is already persisted, $em is an Entity Manager */
$em->remove($product);
$em->flush();
It obviously fails due to integrity constraint violation ("Cannot delete or update a parent row: a foreign key constraint fails (products_tags, CONSTRAINT FK_E3AB5A2CBAD26311 FOREIGN KEY (tag_id) REFERENCES tags (id))'").
When I alter products_tags table adding ON DELETE CASCADE to foreign keys it works as I want. I can EASILY remove TAG ($em->remove($tag)) and PRODUCT ($em->remove($product) that automatically removes referenced rows from products_tags table.
How my code should look like to obtain products_tags table with ON CASCADE DELETE foreign keys? I've already tired with cascade={"all"} but it failed.
I know, I can remove all tag from product's tags collection, but as I mentioned I would like to achieve it in one step, just by calling remove method of entity manager object.
Does Doctrine really lack of that?
Ok, I managed myself by digging in Doctrine2 docs ;) Solution is to add onDelete="cascade" to #JoinColumn.
/**
* #Entity
* #Table(name="products")
**/
class Product {
/** some other fileds here */
/**
* #ManyToMany(targetEntity="Tag")
* #JoinTable(name="products_tags",
* joinColumns={#JoinColumn(name="product_id", referencedColumnName="id", onDelete="cascade")},
* inverseJoinColumns={#JoinColumn(name="tag_id", referencedColumnName="id", onDelete="cascade")}
* )
*/
protected $tags;
}
Note that, cascade={"all"} is managed on object level (in your app), while onDelete="cascade" is on database level.

Doctrine 2 orm:validate-schema fails because of constraint

I have a database with several one-to-many/many-to-one relationships. For example, I have table called Students, and a related table called StudentNotes. The StudentNotes table has a foreign key called student_id. I want the foreign key to have the constraint on delete = cascade.
I set up my Doctrine 2 entities with the property #JoinColumn(on="CASCADE") and updated the database schema. Unfortunately, whenever it does this, it sets the on delete to "restrict". What am I doing wrong?
Here's the relevant code from my Students entity:
/**
* #var Collection Notes
*
* #OneToMany(targetEntity="StudentNotes", mappedBy="student")
* #JoinColumn(onCascade="DELETE")
*/
protected $notes;
And from StudentNotes:
/**
* #var \Entities\Students Student
*
* #ManyToOne(targetEntity="Students", inversedBy="notes")
* #OrderBy({"datetime"="DESC"})
*/
protected $student;
I've even tried adding all of the column information (i.e., name="student_id", referencedColumnName="id"), but nothing changes.
EDIT
I messed up when I originally wrote this: I wrote #JoinColumn(onCascade="DELETE"), when I meant to write #JoinColumn(onDelete="CASCADE"). Either way, this is not working properly: validate-schema fails because the database is not in sync with the schema.
onCascade do not exists, you need instead onDelete="CASCADE".
#JoinColumn(onDelete="CASCADE")
OK, I figured out what I'd forgotten to do!
I had neglected to go back to the command line and run the following command:
php doctrine.php orm:schema-tool:update --force
This did the trick

Delete associated/referenced entity

I have a table product, the items in these table are referenced in tables such as cart_item and order_item as well as shipping_item etc.
All these references are optional (the product_id is set to nullable in those tables).
I need to have a way to delete a product and still keeping the other tables's records. One way I can think of is to go into all those tables, set the product_id to null, then go back to the product table to delete. However, since I may not know all the tables that are referencing to product (many other bundles can have entities that are referencing to this product), is there a way that I can know all these association to loop through and set null?
(Or perhaps there is a better way?)
PS: the idea that this is a shopping cart and the owner may want to remove expired products to clean up but for ordered, shipped items they still need to keep records.
Edit1:
This is the definition of the product reference in the OrderItem entity:
/**
* #var \Product
*
* #ORM\ManyToOne(targetEntity="Product")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
* })
*/
private $product;
The error I'm getting:
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1451
Cannot delete or update a parent row: a foreign key constraint fails
(test.order_item, C ONSTRAINT fk_order_item_product1 FOREIGN KEY
(product_id) REFERENCES product (id) ON DELETE NO ACTION ON
UPDATE NO ACTION)
Edit2:
I initially set onupdate="SET NULL" to the order_item entity and thought that was enough, it was not:
/**
* #var \Product
*
* #ORM\ManyToOne(targetEntity="Product")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="product_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
* })
*/
private $product;
After that, I had to update db schema as well.
Assuming you have the proper relations set up between the owning entity product and the other entities e.g. cart_item that should have a foreign_key, your wanted behaviour is the default for doctrine 2.
Take a look here in the manual
As an example they show the deletion of a User entity and its corresponding Comments
$user = $em->find('User', $deleteUserId);
foreach ($user->getAuthoredComments() AS $comment) {
$em->remove($comment);
}
$em->remove($user);
$em->flush();
The example states:
Without the loop over all the authored comments Doctrine would use an UPDATE statement only to set the foreign key to NULL and only the User would be deleted from the database during the flush()-Operation.
This suggests to me that in your case you actually want that behaviour. So just remove the product entity and doctrine 2 will automatically find all other entities with a foreign_key belonging to that product and will set it to NULL
Edit
Your error message suggests that upon attempted removal of the product entity there are still foreign_keys present, i.e. they have not been set to null properly by Doctrine.
You need to be sure to add the cascade property, specifically remove to your entity relationship. It would look something like the following:
<?php
class Product
{
//...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* #OneToMany(targetEntity="Cart", mappedBy="product", cascade={"remove"})
*/
private $carts;
//...
}

Symfony2 OneToOne relationship becomes either a Unique index or a foreign key?

I'm taking my first steps in Symfony2 entity relations.
I have an entity installation, which each has one meter, and one monitor.
This translates to a uni-directional relationship, which I defined as such:
/**
*
* #ORM\OneToOne(targetEntity="InfoMeter")
* #ORM\JoinColumn(name="meterid", referencedColumnName="id")
*/
private $meter;
/**
*
* #ORM\OneToOne(targetEntity="InstallationsRtu")
* #ORM\JoinColumn(name="monitorid", referencedColumnName="id")
*/
private $monitor;
Each monitor can only be assigned to one installation.
Each meter can be assigned to multiple installations.
When I update my database (app/console doctrine:schema:update --force), there are two outcomes.
In case of the monitor:
Everything goes alright, schema gets updated with a keyname which is prefixed by 'UNIQ_'.
In case of the meter:
I get the following error
PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1' for key 'UNIQ_43D26968860BE41D'
When I look at the table structure after this error occured, I can't find that mentioned constraint, but I do find FK_43D26968860BE41D, i.e. the same, but prefixed with 'FK'.
In the installation table, I now have these listed:
Keyname Type Unique Packed Field Cardinality
PRIMARY BTREE Yes No id 2
UNIQ_43D26968701EC1AB BTREE Yes No monitorid 2
FK_43D26968860BE41D BTREE No No meterid 2
So one saying 'Unique=Yes', and the other saying 'Unique=No'.
To arrive at my question:
How can I decide whether it is a UNIQ index or a FK index?
I assume that Doctrine saw that currently each monitorid is unique in the installation table, but that each meterid occurs several times in the installation table.
Hence it went with UNIQ for the first, and FK for the latter. But can I control this somehow?
If one metter can be assigned to multiple installations, shouldn't you define a OneToMany relationship?
In your installation entity:
/**
* #ORM\ManyToOne(targetEntity="InfoMeter", inversedBy="installations")
* #ORM\JoinColumn(name="infometer_id", referencedColumnName="id")
*/
protected $info_meter;
And then in your InfoMeter entity:
/**
* #ORM\OneToMany(targetEntity="Installation",mappedBy="info_meter")
*/
protected $installations;
Besides, you should add the following to your InfoMeter class constructor:
function __construct() {
[...]
$this->installations = new \Doctrine\Common\Collections\ArrayCollection();
}
I'm sure this approach can be improved depending on how you want the relation between "installations" and "meters", but this should work.