Reading and writing to association tables in CakePHP

When two models have a HABTM relationship (resisting the urge to make a joke) there is necessarily a shared association table. But what if we want to store more information in that table?

While, for example, an association table between Events and Waiters (with Events HABTM Waiters) would be very useful, it would be even more so if we could store in that same table information about whether each waiter had been contacted, and, if so, whether they were available for the event or not.

Unless you tell it otherwise, Cake will assume the association table is strictly for its own use. Its default behavior when saving new data is first to remove all previous data matching the governing model, in this case the event id, before writing the associations in anew. Not good if we’re planning on storing data in that table too.

So we let Cake know it has to share the table by adding the ‘unique’ key to the HABTM definition, with ‘keepExisting’ as the value:

<?php
class Event extends appModel {
    public $hasAndBelongsToMany = array(
        'Waiter' => array(
            'unique' => 'keepExisting'
        )
    );
}
...
?>

Now, when Cake makes changes to the association table it only change the affected rows and leaves everything else as-is.

We also add a primary key column to the association table. Cake doesn’t need one if it’s just storing associations but if we want to access a single record (say, to update it with a particular status) it’s a lot simpler to do so via a primary key.

In terms of reading and writing to the association table it’s helpful to think of it as its own model. The syntax is the same:

<?php

// Reading
$data = $this->Event->EventsWaiter->read('all', array(
    'conditions' => array('event_id' => '123')
));

// Writing
$data = array(
    'id' => $id,
    'status' => 'booked'
);
$this->Event->EventsWaiter->save($data);

?>

Because we include $id in our data array we can use Cake’s handy save function to update our record.