Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 664
biera Avatar asked Sep 28 '13 16:09

biera


1 Answers

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.

like image 119
biera Avatar answered Oct 22 '22 14:10

biera