Originally posted on June 6th, 2006 on another blog.
Cake allow you to relate two tables in a n<->m relation. This is done by the HasAndBelongsToMany relationship (HABTM). In order to implement this relationship you need three table: the two tables to be related and a table for the relationship.
Profiles relation-table Users
-------- -------------- -----
id* --------> profile_id* |------ id*
dx user_id* <---| dx
If you’ll follow the cake naming conventions, implementing this relation is very simple. Let’s quickly sum up the conventions
- Relation table must be named like PluralTable1_PluralTable2
- Tables that give the name to the relation table must be alphabetically sorted. So if you have a users and a profiles table, you’ll get the profiles_users.
- The fields of the relation table must be the primary keys of the related tables and must be named like SingularTable_id. For example profile_id and user_id.
drop table if exists profiles_users;
create table profiles_users(
profile_id int(
unsigned not null default 0,
user_id int(
unsigned not null default 0,
primary key(profile_id, user_id)
)engine=MyISAM;
Now in the model of the tables you need to relate them. This is done by the $hasAndBelongsToMany variable.
class User extends AppModel{
var $name="User";
var $hasAndBelongsToMany = array("Profile");
}
class Profile extends AppModel{
var $name="Profile";
var $displayField = "dx";
var $hasAndBelongsToMany = array("User");
}
Done and done. Now everything should be handled automatically by cake.
Now you just need to insert the fields in the form. Example on the manual use a multiple section <select> tag. In order to create this control you just need the HtmlHelper and selectTag():
$html->selectTag('Profile/Profile',$profiles,null,array('multiple'=>'multiple'))
The only trick is the field name that must be RelatedTable/RelatedTable. In the example, if we are adding a user (users table) and need to save the specified profiles (profiles table), the name to be specified will be Profile/Profile.
In case you don’t like the use of a multiple select and prefer to use more than one checkbox, the workaround is something like the following.
In the needed controller let’s extract the needed values
class UsersController extends AppController{
var $name="Users";
function add(){
...
$this->set("profiles",$this->User->Profile->generateList());
...
}
}
Then in the view something like
<?php
if(is_array($profiles)){
foreach($profiles as $profile => $title){
print "<input type='checkbox' value='{$profile}'
name='data[Profile][Profile][]'>" . $title . "\n";
}
}
?>
Notice the name parameter. I’ve not used the HtmlHelper::checkbox() since I failed to make it works in this case.