5.7.3.6 Creating and Destroying Associations on the Fly
Sometimes it becomes necessary to create and destroy model associations on the fly. This may be for any number of reasons:
- You want to reduce the amount of associated data fetched, but all your associations are on the first level of recursion.
- You want to change the way an association is defined in order to sort or filter associated data.
This association creation and destruction is done using the CakePHP model bindModel() and unbindModel() methods. (There is also a very helpful behavior called "Containable", please refer to manual section about Built-in behaviors for more information). Let's set up a few models so we can see how bindModel() and unbindModel() work. We'll start with two models:
<?php
class Leader extends AppModel {
var $name = 'Leader';
var $hasMany = array(
'Follower' => array(
'className' => 'Follower',
'order' => 'Follower.rank'
)
);
}
?>
<?php
class Follower extends AppModel {
var $name = 'Follower';
}
?>
<?phpclass Leader extends AppModel {var $name = 'Leader';var $hasMany = array('Follower' => array('className' => 'Follower','order' => 'Follower.rank'));}?><?phpclass Follower extends AppModel {var $name = 'Follower';}?>
Now, in the LeadersController, we can use the find() method in the Leader model to fetch a Leader and its associated followers. As you can see above, the association array in the Leader model defines a "Leader hasMany Followers" relationship. For demonstration purposes, let's use unbindModel() to remove that association in a controller action.
function someAction() {
//This fetches Leaders, and their associated Followers
$this->Leader->findAll();
//Let's remove the hasMany...
$this->Leader->unbindModel(
array('hasMany' => array('Follower'))
);
//Now a using a find function will return
//Leaders, with no Followers
$this->Leader->findAll();
//NOTE: unbindModel only affects the very next
//find function. An additional find call will use
//the configured association information.
//We've already used findAll() after unbindModel(),
//so this will fetch Leaders with associated
//Followers once again...
$this->Leader->findAll();
}
function someAction() {//This fetches Leaders, and their associated Followers$this->Leader->findAll();//Let's remove the hasMany...$this->Leader->unbindModel(array('hasMany' => array('Follower')));//Now a using a find function will return//Leaders, with no Followers$this->Leader->findAll();//NOTE: unbindModel only affects the very next//find function. An additional find call will use//the configured association information.//We've already used findAll() after unbindModel(),//so this will fetch Leaders with associated//Followers once again...$this->Leader->findAll();}
One more reminder. Removing or adding associations using bind- and unbindModel() only works for the next model operation only unless the second parameter has been set to false. If the second parameter has been set to false, the bind remains in place for the remainder of the request. Here’s the basic usage pattern for unbindModel():
$this->Model->unbindModel(
array('associationType' => array('associatedModelClassName'))
);
$this->Model->unbindModel(array('associationType' => array('associatedModelClassName')));
Now that we've successfully removed an association on the fly, let's add one. Our as-of-yet unprincipled Leader needs some associated Principles. The model file for our Principle model is bare, except for the var $name statement. Let's associate some Principles to our Leader on the fly (but remember–only for just the following find operation). This function appears in the LeadersController:
function anotherAction() {
//There is no Leader hasMany Principles in
//the leader.php model file, so a find here,
//only fetches Leaders.
$this->Leader->findAll();
//Let's use bindModel() to add a new association
//to the Leader model:
$this->Leader->bindModel(
array('hasMany' => array(
'Principle' => array(
'className' => 'Principle'
)
)
)
);
//Now that we're associated correctly,
//we can use a single find function to fetch
//Leaders with their associated principles:
$this->Leader->findAll();
}
function anotherAction() {//There is no Leader hasMany Principles in//the leader.php model file, so a find here,//only fetches Leaders.$this->Leader->findAll();//Let's use bindModel() to add a new association//to the Leader model:$this->Leader->bindModel(array('hasMany' => array('Principle' => array('className' => 'Principle'))));//Now that we're associated correctly,//we can use a single find function to fetch//Leaders with their associated principles:$this->Leader->findAll();}
There you have it. The basic usage for bindModel() is the encapsulation of a normal association array inside an array whose key is named after the type of association you are trying to create:
$this->Model->bindModel(
array('associationName' => array(
'associatedModelClassName' => array(
// normal association keys go here...
)
)
)
);
$this->Model->bindModel(array('associationName' => array('associatedModelClassName' => array(// normal association keys go here...))));
Even though the newly bound model doesn't need any sort of association definition in it’s model file, it will still need to be correctly keyed in order for the new association to work properly.
