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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With