Alternative Methods for Working with hasAndBelongsToMany Associations

2 : Tutorial

By John Hanauer (glite)
As of 1.1.12.4205, CakePHP's current functionality for working with HABTM associations works very well for most applications, particularly those that lend themselves well to using multiple-selectTags or checkboxes. This article looks at proposed methods for adding or removing join records between two models that don't rely on bulky arrays and that minimize the amount of SQL executed on the database.
Using the code described on the previous page is quite simple. Once you've got it loaded into your app_model, you can do something like the following.

Let's say you have:

User hasAndBelongsToMany UserType

A really simple model function from within your users_controller.php would look something like this:

Controller Class:

<?php 
function assign_user_type($user_id$user_type_id) {
    
$this->User->id $user_id;

    
//alternatively, you can skip the line above and make the following line
    //$this->User->addAssoc('UserType',$user_type_id,$user_id);
    
$this->User->addAssoc('UserType',$user_type_id);

    
$this->setFlash('User has been assigned.');
    
$this->redirect('/users/assignments');
}

function 
unassign_user_type($user_id$user_type_id) {
    
$this->User->id $user_id;

    
//alternatively, you can skip the line above and make the following line
    //$this->User->deleteAssoc('UserType',$user_type_id,$user_id);
    
$this->User->deleteAssoc('UserType',$user_type_id);

    
$this->setFlash('User has been unassigned.');
    
$this->redirect('/users/assignments');
}
?>


Your view could then have code that looked like this:

View Template:


<!-- we're going to assume you've already defined in your view somewhere the function is_assigned() -->
<?php foreach($user_types as $user_type): ?>
    <BR><?php echo $user_type['UserType']['name']; ?>
        <?php if(is_assigned($user,$user_type): ?>
            <?php echo $html->link('Unassign',"/users/unassign_user_type/{$user['User']['id']}/{$user_type['UserType']['id']}");
        <?
php else: ?>
            <?php echo $html->link('Assign',"/users/unassign_user_type/{$user['User']['id']}/{$user_type['UserType']['id']}");
        <?
php endif; ?>     
<?php endforeach;?>


This example uses links, but you could really do whatever, including continuing to use multiple selects. An example might be a selectTag, an Add button, and a table. The table would be populated with UserTypes already assigned to the User, and the selectTag would contain those that have not. When the user selects one or more elements from the multi-select and hits add, the controller gets back somewhere within $this->data an array of ids for the associated model. Just stick that array of ids into the $assoc_ids parameter of addAssoc and deleteAssoc and it'll add each one to the joins for that model without rebuilding the whole group.

Enjoy!

Comments 202

CakePHP Team Comments Author Comments
 

Comment

1 Works great

I haven't tested this in 1.2, but in my current version (1.1.14.4797) it works flawlessly.

Thanks :-)
Posted Apr 20, 2007 by Thomas Winther
 

Comment

2 a

I haven't tested this in 1.2, but in my current version (1.1.14.4797) it works flawlessly.

Thanks :-)


Could you please post an example, I don't get it at all.

Thanks
Posted May 9, 2008 by marcelo garcia
 

Comment

3 Holy simplification batman

Thanks a bunch for this. Helps alot.
Posted May 12, 2008 by afmyers