tags:

views:

490

answers:

1

I have a HABTM relationship between two tables: items and locations, using the table items_locations to join them.

items_locations also stores a bit more information. Here's the schema

items_locations(id, location_id, item_id, quantity)

I'm trying to build a page which shows all the items in one location and lets the user, through a datagrid style interface, edit multiple fields at once:

Location: Factory XYZ
 ___________________________
|___Item____|___Quantity___|
| Widget    |          3   |
| Sprocket  |          1   |
| Doohickey |         15   |
----------------------------

To help with this, I have a controller called InventoryController which has:

var $uses = array('Item', 'Location');  // should I add 'ItemsLocation' ?

How do I build a multidimensional form to edit this data?


Edit:

I'm trying to get my data to look like how Deceze described it below but I'm having problems again...

// inventory_controller.php
function edit($locationId) {

    $this->data = $this->Item->ItemsLocation->find(
        'all',
        array(
            "conditions" => array("location_id" => $locationId)
        )
    );

when I do that, $this->data comes out like this:

Array (
    [0] => Array (
        [ItemsLocation] => Array (
            [id] => 16
            [location_id] => 1
            [item_id] => 1
            [quantity] => 5
        )
    )
    [1] => Array (
        [ItemsLocation] => Array (/* .. etc .. */)
    )
)
+3  A: 

If you're not going to edit data in the Item model, it probably makes most sense to work only on the join model. As such, your form to edit the quantity of each item would look like this:

echo $form->create('ItemsLocation');

// foreach Item at Location:

echo $form->input('ItemsLocation.0.id'); // automatically hidden
echo $form->input('ItemsLocation.0.quantity');

Increase the counter (.0., .1., ...) for each record. What you should be receiving in your controllers $this->data should look like this:

array(
    'ItemsLocation' => array(
        0 => array(
            'id' => 1,
            'quantity' => 42
        ),
        1 => array(
            ...

You can then simply save this like any other model record: $this->Item->ItemsLocation->saveAll($this->data). Adding an Item to a Location is not much different, you just leave off the id and let the user select the item_id.

array(
    'location_id' => 42,  // prepopulated by hidden field
    'item_id' => 1        // user selected
    'quantity' => 242
)

If you want to edit the data of the Item model and save it with a corresponding ItemsLocation record at the same time, dive into the Saving Related Model Data (HABTM) chapter. Be careful of this:

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.

And:

3.7.6.5 hasAndBelongsToMany (HABTM)

unique: If true (default value) cake will first delete existing relationship records in the foreign keys table before inserting new ones, when updating a record. So existing associations need to be passed again when updating.


Re: Comments/Edit

I don't know off the top of my head if the FormHelper is intelligent enough to autofill Model.0.field fields from a [0][Model][field] structured array. If not, you could easily manipulate the results yourself:

foreach ($this->data as &$data) {
    $data = $data['ItemsLocation'];
}
$this->data = array('ItemsLocation' => $this->data);

That would give you the right structure, but it's not very nice admittedly. If anybody has a more Cakey way to do it, I'm all ears. :)

deceze
that makes a lot more sense now.. though I'm still hitting a brick wall. Mind taking a look at my edit to the original post?
nickf
Well, the format will be slightly different when you **find** data. What I'm talking about is the right format for **POSTing a form to the controller.** Try a `debug($this->data)` in the controller upon receiving the POST data.
deceze
but won't you need it in that format for the view to be able to do something with like: `$form->input('ItemsLocation.0.id')`
nickf
This may help a little. http://teknoid.wordpress.com/2008/09/24/saving-extra-fields-in-the-join-table-for-habtm-models/
Dooltaz