Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cakephp's beforeSave not retaining new data when using saveAll?

I have a relatively simple entries model with just five fields:

  1. id
  2. type (what datatype this entry is)
  3. amount (how many of whatever type it is)
  4. unit (the unit of the type)
  5. date (the datettime when this entry was entered)
  6. user_id (the id of the user who enters

So, nothing fancy. Now a single form can have multiple entries (both already existing ones and new ones just created), the form is extended via ajax calls.

When I submit the form $this->data looks like this:

Array
(
    [Entry] => Array
        (
            [date] => 2011-01-07
            [0] => Array
                (
                    [id] => 1
                    [type] => Eat
                    [amount] => 1 Steak, one baked potatoe
                    [unit] => lunch
                    [time] => Array
                        (
                            [hour] => 13
                            [min] => 31
                        )

                )

            [1] => Array
                (
                    [type] => weight
                    [amount] => 78.5
                    [unit] => KG
                    [time] => Array
                        (
                            [hour] => 22
                            [min] => 22
                        )

                )

        )

)

The first entry in $this->data['Entry']['date'] is the date that shall be used by ALL the entries. And since also the user_id is missing I created a "beforeSave" function in the entry-model. It looks like this:

function beforeSave() {
    App::import('Component','Session');
    $this->Session = new SessionComponent();

    if (isset($this->data) && isset($this->data['Entry'])) {
        $date = $this->data['Entry']['date'];
        unset($this->data['Entry']['date']);

        foreach ($this->data['Entry'] as $n => $entry) {
            if (is_array($entry)) {
             $this->data['Entry'][$n]['date'] = $date . ' ' . $entry['time']['hour'] . ':' . $entry['time']['min'] . ':00';
                $this->data['Entry'][$n]['user_id'] = $this->Session->read('Auth.User.id');
            }
        }
        debug($this->data);

    }
    return true;
}

I remove the date, add it together with the time entry of the user, thus creating a mysql datetime entry and add the user_id of the logged in user. Straightforward, really. The resulting array (as output by that last debug()) looks like the following:

Array
(
    [Entry] => Array
    (
        [0] => Array
            (
                [id] => 1
                [type] => Eat
                [amount] => 1 Steak, 1 baked potatoe
                [unit] => lunch
                [time] => Array
                    (
                        [hour] => 09
                        [min] => 31
                    )

                [date] => 2011-01-07 09:31:00
                [user_id] => 2
            )

        [1] => Array
            (
                [type] => Weight
                [amount] => 78.5
                [unit] => KG
                [time] => Array
                    (
                        [hour] => 22
                        [min] => 22
                    )

                [date] => 2011-01-07 22:22:00
                [user_id] => 2
            )

    )

)

So it look exactly like I want it to look and it should be easily saved. But when I use $this->Entry->saveAll($this->data['Entry']) to save all the entries, not only does it not work, but when I debug $this->data directly after the saveAll, it looks exactly like before the saveAll function - the date is back in the array, the entries do not have a date or user_id entry.

I can see that beforeSave is called, I can see that it changes $this->data, but somewhere between the end of beforeSave and the usage of "saveAll" all my changes get lost and $this->data is reverted to it's original state. Therefore no saving takes place.

like image 288
Sorcy Avatar asked Jan 20 '23 16:01

Sorcy


1 Answers

I had similar problem trying to implement some sanitization with hasMany through. As it turns out the problem lies in how saveAll() works. The $this->data in controller isn't the same as $this->data in Model::beforeSave() callback and the two are not synchronized. When You call Model::saveAll() passing $this->data from the controller You explicitly give the model "the old version" of data array. And when You look into saveAll definition in cake/libs/model/model.php it just does:

if (empty($data)) {
   $data = $this->data
}

and nothing more to sync up the modified $data array in model and passed by parameter $data form the controller. The solution is to not pass the data to saveAll() but instead set the data on model first using Model::set() and then call Model::saveAll() (without the $data parameter).

like image 190
snowball Avatar answered Jan 30 '23 12:01

snowball