3.7.4.2 Saving Related Model Data (HABTM)
Saving models that are associated by hasOne, belongsTo, and hasMany is pretty simple: you just populate the foreign key field with the ID of the associated model. Once that's done, you just call the save() method on the model, and everything gets linked up correctly.
With HABTM, you need to set the ID of the associated model in your data array. We'll build a form that creates a new tag and associates it on the fly with some recipe.
The simplest form might look something like this (we'll assume that $recipe_id is already set to something):
<?php echo $form->create('Tag');?>
<?php echo $form->input(
'Recipe.id',
array('type'=>'hidden', 'value' => $recipe_id)); ?>
<?php echo $form->input('Tag.name'); ?>
<?php echo $form->end('Add Tag'); ?>
<?php echo $form->create('Tag');?><?php echo $form->input('Recipe.id',array('type'=>'hidden', 'value' => $recipe_id)); ?><?php echo $form->input('Tag.name'); ?><?php echo $form->end('Add Tag'); ?>
In this example, you can see the Recipe.id hidden field whose value is set to the ID of the recipe we want to link the tag to.
When the save() method is invoked within the controller, it'll automatically save the HABTM data to the database.
function add() {
//Save the association
if ($this->Tag->save($this->data)) {
//do something on success
}
}
function add() {//Save the associationif ($this->Tag->save($this->data)) {//do something on success}}
With the preceding code, our new Tag is created and associated with a Recipe, whose ID was set in $this->data['Recipe']['id'].
Other ways we might want to present our associated data can include a select drop down list. The data can be pulled from the model using the find('list') method and assigned to a view variable of the model name. An input with the same name will automatically pull in this data into a <select>.
// in the controller:
$this->set('tags', $this->Recipe->Tag->find('list'));
// in the view:
$form->input('tags');
// in the controller:$this->set('tags', $this->Recipe->Tag->find('list'));// in the view:$form->input('tags');
A more likely scenario with a HABTM relationship would include a <select> set to allow multiple selections. For example, a Recipe can have multiple Tags assigned to it. In this case, the data is pulled out of the model the same way, but the form input is declared slightly different. The tag name is defined using the ModelName convention.
// in the controller:
$this->set('tags', $this->Recipe->Tag->find('list'));
// in the view:
$form->input('Tag');
// in the controller:$this->set('tags', $this->Recipe->Tag->find('list'));// in the view:$form->input('Tag');
Using the preceding code, a multiple select drop down is created, allowing for multiple choices to automatically be saved to the existing Recipe being added or saved to the database.
What to do when HABTM becomes complicated?
By default when saving a HasAndBelongsToMany relationship, Cake will delete all rows on the join table before saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2 children. The Club will only have 2 Children, not 12.
Also note that if you want to add more fields to the join (when it was created or meta information) this is possible with HABTM join tables, but it is important to understand that you have an easy option.
HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo association.
Consider this example:
Child hasAndBelongsToMany Club
Child hasAndBelongsToMany Club
Another way to look at this is adding a Membership model:
Child hasMany Membership Membership belongsTo Child, Club Club hasMany Membership.
Child hasMany MembershipMembership belongsTo Child, ClubClub hasMany Membership.
These two examples are almost the exact same. They use the same amount and named fields in the database and the same amount of models. The important differences are that the "join" model is named differently and it's behavior is more predictable.
When your join table contains extra fields besides two foreign keys, in most cases its easier to make a model for the join table and setup hasMany, belongsTo associations as shown in example above instead of using HABTM association.
