Reputation: 24077
I have 2 models, Client
and Security
. They are associated with a HATBTM relationship.
I have made a join table called clients_securities
. So a Client
can have many securities and a Security
can belong to many Client
s.
Here are my relationship definitions:
//Client Model:
public $hasAndBelongsToMany = array(
'Security' =>
array(
'className' => 'Security',
'joinTable' => 'clients_securities',
'foreignKey' => 'client_id',
'associationForeignKey' => 'security_id',
'unique' => true,
)
);
//Security Model:
public $hasAndBelongsToMany = array(
'Client' =>
array(
'className' => 'Client',
'joinTable' => 'clients_securities',
'foreignKey' => 'security_id',
'associationForeignKey' => 'client_id',
'unique' => true,
)
);
I then made an edit action in my clients controller and did this:
public function edit($id = null) {
if (!$id) {
throw new NotFoundException(__('Invalid client'));
}
$client = $this->Client->findById($id);
if (!$client) {
throw new NotFoundException(__('Invalid client'));
}
if ($this->request->is('post') || $this->request->is('put')) {
$this->Client->id = $id;
if ($this->Client->saveAll($this->request->data)) {
$this->Session->setFlash(__('Client has been updated.'));
return $this->redirect(array('action' => 'edit', $id));
}
$this->Session->setFlash(__('Unable to update client.'));
}
if (!$this->request->data) {
$this->request->data = $client;
$this->set('securities', $this->Client->Security->find('list'));
}
}
In my edit view I build the form using the HtmlHelper, adding the client table fields and generating the multiple select with:
echo $this->Form->create('Client'); //start the form for the client model
echo $this->Form->input('Security'); //generates a multi-select popuplated with securities
In addition I also have normal direct form inputs for the Client
in the same form. e.g.
echo $this->Form->input('name'); //Input for the client name
echo $this->Form->input('currency'); //Input for the client currency
...
All these inputs get generated and populated with the correct values when the form is rendered but only the direct Client data is saved, not the HABTM data from the multi-select.
When I submit the form the clients_securities
table is not populated with the join IDs.
What do I need to do to save it correctly and have then have the saved securities be pre-selected when I reload the "edit" view.
Edit: To clarify things, here is a pr()
of $this->request->data
.
(The values ("ALLCMCT LX Equity") are the correct foreign keys for the securities
table):
Array
(
[Client] => Array
(
[id] => 1212
[name] => Example Client
[currency] => GBP
[partner] =>
[risk_category_id] => 4
[client_code_id] => 1
[min_cash_balance] => 0
[active] => 1
[model] => 0
[institutional] => 0
[non_uk_situs] => 0
[reporting_status] => 0
)
[Security] => Array
(
[Security] => Array
(
[0] => .1muslib3 index
[1] => .eurib3 index
[2] => .ukcpi2 index
)
)
)
So essentially, say my client id was 12345, Cake should insert 2 records in the clients_securities
table like so:
id | client_id | security_id
----------------------------
1 | 12345 | ALLCMCT LX Equity
2 | 12345 | APMKNTD LX Equity
If I manually add some join records into clients_securities
, when I go to edit a client the securities in the multi-select come up correctly pre-selected showing that the data is being read from the join table. When I save the form it actually removes the join records but does not save new ones.
Additional note: My security IDs are stored as CHAR(36) if that has any effect. This is not an auto-increment field, it contains the ticker of the security and each is unique.
Upvotes: 11
Views: 10982
Reputation: 9398
The problem is in the format of the security_id
and Security.id
: char is unsupported.
I found this ticket (but I don't find any reference in the documentation)
you must create another column with a numeric id
to use in the HABTM association
Upvotes: 5
Reputation: 1061
Its not populating mediater table because After populating both models It does not have their id to populate mediater table. So user beforeSave and afterSave function which will populate your mediator table. Add beforesave function in Client model
public function beforeSave($options = array()) {
parent::beforeSave($options = array());
foreach (array_keys($this->hasAndBelongsToMany) as $model):
if(isset($this->data[$this->alias][$model])):
$this->data[$model][$model] = $this->data[$this->alias][$model];
unset($this->data[$this->alias][$model]);
endif;
endforeach;
return true;
}
And in Security user AfterSave function,
public $securityIdArray = array();
public function afterSave($created) {
if($created) {
$this->securityIdArray[] = $this->getInsertID();
}
return true;
}
First save security and use $securityIdArray to get id of security after $this->Model->save in your controller.
$this->Security->securityIdArray[] = $void['Security']['id'];
And yah As @Arilia said Modify you forum input field
echo $this->Form->input('Security.Security');
and don't make saperate forms for both models
Upvotes: 0
Reputation: 9398
Maybe I'm worng but I guess you forgot to create a hidden input with the Client id in your view
echo $this->Form->hidden('Client.id');
Upvotes: 0
Reputation: 2488
I'll give it a try...
You forgot $this->Client->read(); after $this->Client->id = $id; in the edit() function (the internal of $this->Client are not initialized, so no client_id is present at the moment when save is done). The call "findById" will return only an array, but won't read the data to the object $this->Client.
Just a good educated guess..
Upvotes: 0
Reputation: 91
modify your form input like so:
echo $this->Form->input('Security.Security');
Upvotes: 1
Reputation: 685
<?php
Model::saveAssociated(array $data = null, array $options = array());
or
Model::saveAll(array $data = null, array $options = array());
?>
For more info please visit: http://book.cakephp.org/2.0/en/models/saving-your-data.html
Upvotes: 0