harryg
harryg

Reputation: 24077

CakePHP saving HABTM data

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 Clients.

enter image description here

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

Answers (6)

arilia
arilia

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

karmicdice
karmicdice

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

arilia
arilia

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

Honk der Hase
Honk der Hase

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

jmcneese
jmcneese

Reputation: 91

modify your form input like so:

echo $this->Form->input('Security.Security');

Upvotes: 1

Vinod Patidar
Vinod Patidar

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

Related Questions