Save multiple records for one model in CakePHP

19,035

Solution 1

First off, this database design needs to be normalized.

It seems to me like a Notification can have many Users related to it. At the same time, a User can have many Notifications. Therefore,

  • Introduce a join table named users_notifications.
  • Implement the HABTM Relationship: Notification hasAndBelongsToMany User

In the view, you can simply use the following code to automagically bring up the multi-select form to grab user ids:

echo $this->Form->input('User');

The data that is sent to the controller will be of the form:

Array(
    [Notification] => Array
        (
            [subject] => subject
            [content] => contentcontentcontentcontentcontentcontent
        ),
    [User] => Array
        (
            [User] => Array
                (
                    [0] => 4
                    [1] => 6
                )
         )
)

Now, all you have to do is called the saveAll() function instead of save().

$this->Notification->saveAll($this->data);

And that's the Cake way to do it!

Solution 2

Those values have to be repeat like this

Array([Notification] => Array(
        [0] => Array
            (
                        [user_id] => 4
                        [subject] => subjects
                        [content] => content
                    )
        [1] => Array
            (
                        [user_id] => 6
                        [subject] => subject
                        [content] => contents
                    )
            )
)


$this->Notification->saveAll($data['Notification']); // should work

If you don't pass any column value, this cake will just ignore it

Solution 3

You're going to have to massage your form's output to suit Model::saveAll. In your controller:

function action_name()
{
    if ($this->data) {

        if ($this->Notification->saveMany($this->data)) {
            // success! :-)
        } else {
            // failure :-(
        }
    }
}

And in your model:

function saveMany($data)
{
    $saveable = array('Notification'=>array());

    foreach ($data['Notification']['user_id'] as $user_id) {

        $saveable['Notification'][] = Set::merge($data['Notification'], array('user_id' => $user_id));
    }

    return $this->saveAll($saveable);
}

The benefit here is your controller still knows nothing about your model's schema, which it shouldn't.

In fact, you could probably redefine saveAll in your model, which hands off correctly formatted input arrays to parent::saveAll, but handles special cases itself.

Solution 4

There might be a more cakey way of doing this, but I've used this kind of technique: First, change the form so that you get an array like this:

Array(
    [Notification] => Array
        (
            [subject] => subject
            [content] => contentcontentcontentcontentcontentcontent
        ),
    [selected_users] => Array
        (
            [id] => Array
                (
                    [0] => 4
                    [1] => 6
                )
         )
)

(Just change the multiselect input's name to selected_users.id)

Then loop through the user ids and save each record individually:

foreach( $this->data[ 'selected_users' ][ 'id' ] as $userId ) {
    $this->data[ 'Notification' ][ 'user_id' ] = $userId;
    $this->Notification->create();  // initializes a new instance
    $this->Notification->save( $this->data );
}
Share:
19,035
linkyndy
Author by

linkyndy

Passionate about everything web-related. Love cycling, snooker, swimming and traveling.

Updated on June 18, 2022

Comments

  • linkyndy
    linkyndy almost 2 years

    I would like to save several records for one model. This would have been done pretty easily with saveAll() if it hadn't been for a problem:

    I have a notification form, where I select multiple users from a <select> and two fields, for subject and content respectively. Now, when I output what $this->data contains, I have:

    Array([Notification] => Array
        (
            [user_id] => Array
                (
                    [0] => 4
                    [1] => 6
                )
    
            [subject] => subject
            [content] => the-content-here
        )
    )
    

    I've read on Cake 1.3 book, that in order to save multiple records for a model, you have to have the $this->data something like:

    Array([Article] => Array(
            [0] => Array
                (
                            [title] => title 1
                        )
            [1] => Array
                (
                            [title] => title 2
                        )
                )
    )
    

    So, how do I 'share' the subject and content to all selected users?

  • linkyndy
    linkyndy over 13 years
    Thank you for your answer. I thought it was a more 'cakey' method for doing this. But, I'll go the 'straight-forward' way.
  • linkyndy
    linkyndy over 13 years
    Thank you for your reply. It is a good suggestion to make a separate method, will do that!
  • linkyndy
    linkyndy over 13 years
    I put this code on my server and it seems that the saveAll() doesn't work. The new array looks smooth, I pass it to the saveAll(), but it doesn't save anything. I removed the validation within this method but with no success. What could the problem be?
  • linkyndy
    linkyndy over 13 years
    Thank you for your answer! I thought that having a Users hasMany Notifications is enough. But now I understand that I also need a Notifications hasMany Users is order to properly submit the form. Have only a question, though: I have a Users hasAndBelongsToMany Notifications relationship, and I have the UsersController and the NotificationsController. Do I need the UsersNotificationsController for something? Or I'll use $this->User->Notification, respectively $this->Notification->User when the relationship is needed?
  • RabidFire
    RabidFire over 13 years
    You might need the model UsersNotification if you begin to add complexity to the join relationship.
  • RabidFire
    RabidFire over 13 years
    For example: If you want to store whether a user has read a notification, your join table becomes: id - user_id - notification_id - read. Now, you would have to model this "through" relationship as a separate entity. You can read more about it here: book.cakephp.org/view/1039/…
  • linkyndy
    linkyndy over 13 years
    Thanks again. Still, I have one more question: why are all associations in the join table deleted when a new association is inserted in the table? It doesn't make any sense to me. And to stop this 'wierd' behaviour, I must have 'unique' = false in my HABTM's relationship define array?
  • RabidFire
    RabidFire over 13 years
    HABTM relationships are based on the Post-Tag analogy, where posts have unique tags (same tag doesn't appear twice). So, if you'd want to edit your post's tags, this behavior wouldn't be weird at all! :) Using 'unique' = false is the right way to go. It's used when adding new individual tags to a post.
  • linkyndy
    linkyndy over 13 years
    Thank you for clarifying :) I'll make the changes you suggested and if I have problems, I will ask again - hope you don't mind :)