CakePHP saving many objects with HABTM associations using saveAll

Saving many objects with their HABTM relationships using CakePHP’s saveAll is not as straight forward as one may think.

After playing around with results from $this->data, I’ve found the format that works.

In the example below, I’m saving multiple Users who have and belong to many Categories (HABTM). John belongs to Category’s 1 and 3, and Matthew belongs to categories 5 and 6.

Below is the array structure I passed to saveAll to get the save working.

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [first_name] => John
                    [last_name] => Smith
                    [email] => john@aol.com
                    [password] => fakepassword
                )
 
            [Category] => Array
                (
                    [Category] => Array
                        (
                            [0] => 1
                            [1] => 3
                        )
 
                )
 
        )
 
    [1] => Array
        (
            [User] => Array
                (
                    [first_name] => Matthew
                    [last_name] => Johnson
                    [email] => matt@aol.com
                    [password] => fakepassword
                )
 
            [Category] => Array
                (
                    [Category] => Array
                        (
                            [0] => 5
                            [1] => 6
                        )
 
                )
 
        )
 
)

Know of a better, more logical way? Please let me know in this comments. This structure doesn’t feel natural to me at all.

7 responses to “CakePHP saving many objects with HABTM associations using saveAll”

  1. tre says:

    Hi. I am trying to do the same thing, but got stuck and wondering if you can share the View snippet you used to achieve this structure?

    I use Cake 1.2.

    if I use $form->input(…..); ,

    it just won’t create a:

    News.0.Category.0.category_type_id
    News.0.Category.1.category_type_id
    News.0.Category.2.category_type_id
    News.1.Category.0.category_type_id
    News.1.Category.1.category_type_id
    News.3.Category.0.category_type_id

    it will create a
    Category.0.category_type_id
    Category.1.category_type_id
    Category.2.category_type_id

    and take the ‘Category’ for the last ‘Report’ item in a form loop

    I was almost convinced that Cake doesn’t do more than 3 level of ‘association nesting’ until I saw your blog post. Do you have any suggestion for this? Thanks in advance, :)

    • tre says:

      sorry i mean

      “and take the ‘Category’ for the last ‘News’ item in a form loop”

    • steve says:

      Hi Tre,

      I’m actually just saving one level of association – a HABTM association, but I’m doing it on multiple objects.

      Also, this was used in an import function from a CSV file, so I didn’t actually create a view for it.

      Regardless, I could look at your problem but am I having a hard time understanding it, can you post your model associations and view code in pastebin or gist?

  2. Jacob says:

    Thank you so much, exactly this is not clear from the docs, and I was attempting using an id key where appearantly “no key” is the solution.

  3. Herman says:

    I try to use the same structure… But in midget table I get 0 in one of fereign key column :(
    $this->Task->bindModel(array(
    ‘hasAndBelongsToMany’ => array(
    ‘Tag’ => array(
    ‘className’ => ‘Tag’,
    ‘joinTable’ => ‘tags_tasks’,
    ‘foreignKey’ => ‘task_id’,
    ‘associationForeignKey’ => ‘tag_id’,
    ‘unique’ => false
    )
    )
    ));
    $this->data dump:

    array(2) {
    [“Task”]=>
    array(5) {
    [“unparsed”]=>
    string(36) “test ^2012-05-17 !3 test #tag1 #tag2”
    [“id”]=>
    string(1) “1”
    [“name”]=>
    string(9) “test test”
    [“priority”]=>
    string(1) “3”
    [“due_date”]=>
    string(10) “2012-05-17”
    }
    [“Tag”]=>
    array(1) {
    [“Tag”]=>
    array(2) {
    [1]=>
    string(1) “1”
    [2]=>
    string(1) “2”
    }
    }
    }

    result in tags_tasks table:
    tag_id | task_id
    0 ———– 0
    0 ———– 1
    0 ———– 3

    WHY??? Where does Cake found Task 3 if I have only one ?

  4. Blake says:

    Thanks a lot for sharing this!

  5. Yes, it’s strange but that is how it works. You could get this format by baking scaffolded view and making “pr” before saving data to see that CakePHP saves ManyToMany exactly the same way.

Leave a Reply

Your email address will not be published. Required fields are marked *