I saw few documentation about the HABTM relation, so I thought of writing some few things I have found out after my researches. Here I will show a HABTM with two tables, Posts and Tags. A post can have many tags and of course a tag can belong to several posts.
Database (posts table taken from CakePHP blog tutorial)
CREATE TABLE posts ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(50), body TEXT, created DATETIME DEFAULT NULL, modified DATETIME DEFAULT NULL ); CREATE TABLE tags ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL ); CREATE TABLE posts_tags ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id INT NOT NULL, tag_id INT NOT NULL );
This is just example tables, you may add your engine, encoding and put the fields you want.
Just remember that the associated table name must by convention have the name of the two tables you link, and in alphabetical order. It is posts_tags and not tags_posts ! ! ! But you are not forced to use conventions, they are just convenient, and there I will use them.
After the database, here comes the time of setting the models. I will only show the important part about HABTM in them.
public $hasAndBelongsToMany = array ( 'Tag' => array ( 'className' => 'Tag', 'joinTable' => 'posts_tags', 'foreign_key' => 'post_id', 'associationForeignKey' => 'tag_id', 'unique' => false ) );
public $hasAndBelongsToMany = array ( 'Post' => array ( 'className' => 'Post', 'joinTable' => 'posts_tags', 'foreign_key' => 'tag_id', 'associationForeignKey' => 'post_id', 'unique' => false ) );
You can also create a model called PostsTag, if you need. Again with the convention, alphabetical and plural for the first model, not the second, it is PostsTag and not TagsPost.
Now let’s go to the controller Post. For what I saw, you can’t save your new post and its tags in one shot. It comes from the fact that you need the id’s post to save the association.
So first we save the post, nothing new here. You may just send the Post content, test if it was successful, I just go to the essential.
Now we have the id of the post in $this->Post->id. We just have to save our tags and add the id in its structure. Here we assume $tags is an array of tag you get from your view.
foreach($tags as &$tag) $tag['Post']['id'] = $this->Post->id; $this->Post->Tag->saveAll($tags);
There we had the id to all our tags and save them all in one shot. What’s magic here is that cakePHP directly save the tags but also the associations between the tags and the post.
Now let’s say you want to erase all the associated tags of one specific post, you can do like this.
$this->Post->PostsTag->deleteAll(array ('PostsTag.post_id' => $this->Post->id), false);
And if you just want to erase one association, just specify the id of the post and the id of the tag you don’t want anymore. And voilà !
HABTM are tricky, I may not be very clear to explain this point, and I surely don’t know the whole about it, don’t hesitate on correcting me if needed 😉