Skip to content

Commit

Permalink
Fix transactions do not get rollbacked in saveAssociated/saveMany
Browse files Browse the repository at this point in the history
Refs #2849
  • Loading branch information
chinpei215 committed Aug 2, 2014
1 parent 3d77ce5 commit 799500c
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 126 deletions.
276 changes: 150 additions & 126 deletions lib/Cake/Model/Model.php
Expand Up @@ -2215,6 +2215,7 @@ public function saveAll($data = array(), $options = array()) {
* @return mixed If atomic: True on success, or false on failure.
* Otherwise: array similar to the $data array passed, but values are set to true/false
* depending on whether each record saved successfully.
* @throws PDOException
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savemany-array-data-null-array-options-array
*/
public function saveMany($data = null, $options = array()) {
Expand Down Expand Up @@ -2245,47 +2246,58 @@ public function saveMany($data = null, $options = array()) {
if ($options['atomic']) {
$db = $this->getDataSource();
$transactionBegun = $db->begin();
} else {
$transactionBegun = false;
}

$return = array();
foreach ($data as $key => $record) {
$validates = $this->create(null) !== null;
$saved = false;
if ($validates) {
if ($options['deep']) {
$saved = $this->saveAssociated($record, array_merge($options, array('atomic' => false)));
} else {
$saved = $this->save($record, $options);
try {
$return = array();
foreach ($data as $key => $record) {
$validates = $this->create(null) !== null;
$saved = false;
if ($validates) {
if ($options['deep']) {
$saved = $this->saveAssociated($record, array_merge($options, array('atomic' => false)));
} else {
$saved = $this->save($record, $options);
}
}

$validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
if (!$validates) {
$validationErrors[$key] = $this->validationErrors;
}
}

$validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
if (!$validates) {
$validationErrors[$key] = $this->validationErrors;
if (!$options['atomic']) {
$return[$key] = $validates;
} elseif (!$validates) {
break;
}
}

$this->validationErrors = $validationErrors;

if (!$options['atomic']) {
$return[$key] = $validates;
} elseif (!$validates) {
break;
return $return;
}
}

$this->validationErrors = $validationErrors;

if (!$options['atomic']) {
return $return;
}
if ($validates) {
if ($transactionBegun) {
return $db->commit() !== false;
}
return true;
}

if ($validates) {
if ($transactionBegun) {
return $db->commit() !== false;
$db->rollback();
}
return true;
return false;
} catch (Exception $e) {
if ($transactionBegun) {
$db->rollback();
}
throw $e;
}

$db->rollback();
return false;
}

/**
Expand Down Expand Up @@ -2337,6 +2349,7 @@ public function validateMany(&$data, $options = array()) {
* @return mixed If atomic: True on success, or false on failure.
* Otherwise: array similar to the $data array passed, but values are set to true/false
* depending on whether each record saved successfully.
* @throws PDOException
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
*/
public function saveAssociated($data = null, $options = array()) {
Expand Down Expand Up @@ -2368,133 +2381,144 @@ public function saveAssociated($data = null, $options = array()) {
if ($options['atomic']) {
$db = $this->getDataSource();
$transactionBegun = $db->begin();
} else {
$transactionBegun = false;
}

$associations = $this->getAssociated();
$return = array();
$validates = true;
foreach ($data as $association => $values) {
$isEmpty = empty($values) || (isset($values[$association]) && empty($values[$association]));
if ($isEmpty || !isset($associations[$association]) || $associations[$association] !== 'belongsTo') {
continue;
}
try {
$associations = $this->getAssociated();
$return = array();
$validates = true;
foreach ($data as $association => $values) {
$isEmpty = empty($values) || (isset($values[$association]) && empty($values[$association]));
if ($isEmpty || !isset($associations[$association]) || $associations[$association] !== 'belongsTo') {
continue;
}

$Model = $this->{$association};
$Model = $this->{$association};

$validates = $Model->create(null) !== null;
$saved = false;
if ($validates) {
if ($options['deep']) {
$saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
} else {
$saved = $Model->save($values, array('atomic' => false) + $options);
$validates = $Model->create(null) !== null;
$saved = false;
if ($validates) {
if ($options['deep']) {
$saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
} else {
$saved = $Model->save($values, array('atomic' => false) + $options);
}
$validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true)));
}
$validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true)));
}

if ($validates) {
$key = $this->belongsTo[$association]['foreignKey'];
if (isset($data[$this->alias])) {
$data[$this->alias][$key] = $Model->id;
if ($validates) {
$key = $this->belongsTo[$association]['foreignKey'];
if (isset($data[$this->alias])) {
$data[$this->alias][$key] = $Model->id;
} else {
$data = array_merge(array($key => $Model->id), $data, array($key => $Model->id));
}
$options = $this->_addToWhiteList($key, $options);
} else {
$data = array_merge(array($key => $Model->id), $data, array($key => $Model->id));
$validationErrors[$association] = $Model->validationErrors;
}
$options = $this->_addToWhiteList($key, $options);
} else {
$validationErrors[$association] = $Model->validationErrors;
}

$return[$association] = $validates;
}

if ($validates && !($this->create(null) !== null && $this->save($data, $options))) {
$validationErrors[$this->alias] = $this->validationErrors;
$validates = false;
}
$return[$this->alias] = $validates;

foreach ($data as $association => $values) {
if (!$validates) {
break;
$return[$association] = $validates;
}

$isEmpty = empty($values) || (isset($values[$association]) && empty($values[$association]));
if ($isEmpty || !isset($associations[$association])) {
continue;
if ($validates && !($this->create(null) !== null && $this->save($data, $options))) {
$validationErrors[$this->alias] = $this->validationErrors;
$validates = false;
}
$return[$this->alias] = $validates;

$Model = $this->{$association};
foreach ($data as $association => $values) {
if (!$validates) {
break;
}

$type = $associations[$association];
$key = $this->{$type}[$association]['foreignKey'];
switch ($type) {
case 'hasOne':
if (isset($values[$association])) {
$values[$association][$key] = $this->id;
} else {
$values = array_merge(array($key => $this->id), $values, array($key => $this->id));
}
$isEmpty = empty($values) || (isset($values[$association]) && empty($values[$association]));
if ($isEmpty || !isset($associations[$association])) {
continue;
}

$validates = $Model->create(null) !== null;
$saved = false;
$Model = $this->{$association};

if ($validates) {
$options = $Model->_addToWhiteList($key, $options);
if ($options['deep']) {
$saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
$type = $associations[$association];
$key = $this->{$type}[$association]['foreignKey'];
switch ($type) {
case 'hasOne':
if (isset($values[$association])) {
$values[$association][$key] = $this->id;
} else {
$saved = $Model->save($values, $options);
$values = array_merge(array($key => $this->id), $values, array($key => $this->id));
}
}

$validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
if (!$validates) {
$validationErrors[$association] = $Model->validationErrors;
}
$validates = $Model->create(null) !== null;
$saved = false;

$return[$association] = $validates;
break;
case 'hasMany':
foreach ($values as $i => $value) {
if (isset($values[$i][$association])) {
$values[$i][$association][$key] = $this->id;
} else {
$values[$i] = array_merge(array($key => $this->id), $value, array($key => $this->id));
if ($validates) {
$options = $Model->_addToWhiteList($key, $options);
if ($options['deep']) {
$saved = $Model->saveAssociated($values, array('atomic' => false) + $options);
} else {
$saved = $Model->save($values, $options);
}
}
}

$options = $Model->_addToWhiteList($key, $options);
$_return = $Model->saveMany($values, array('atomic' => false) + $options);
if (in_array(false, $_return, true)) {
$validationErrors[$association] = $Model->validationErrors;
$validates = false;
}
$validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
if (!$validates) {
$validationErrors[$association] = $Model->validationErrors;
}

$return[$association] = $_return;
break;
$return[$association] = $validates;
break;
case 'hasMany':
foreach ($values as $i => $value) {
if (isset($values[$i][$association])) {
$values[$i][$association][$key] = $this->id;
} else {
$values[$i] = array_merge(array($key => $this->id), $value, array($key => $this->id));
}
}

$options = $Model->_addToWhiteList($key, $options);
$_return = $Model->saveMany($values, array('atomic' => false) + $options);
if (in_array(false, $_return, true)) {
$validationErrors[$association] = $Model->validationErrors;
$validates = false;
}

$return[$association] = $_return;
break;
}
}
}
$this->validationErrors = $validationErrors;
$this->validationErrors = $validationErrors;

if (isset($validationErrors[$this->alias])) {
$this->validationErrors = $validationErrors[$this->alias];
unset($validationErrors[$this->alias]);
$this->validationErrors = array_merge($this->validationErrors, $validationErrors);
}
if (isset($validationErrors[$this->alias])) {
$this->validationErrors = $validationErrors[$this->alias];
unset($validationErrors[$this->alias]);
$this->validationErrors = array_merge($this->validationErrors, $validationErrors);
}

if (!$options['atomic']) {
return $return;
}
if ($validates) {
if ($transactionBegun) {
return $db->commit() !== false;
if (!$options['atomic']) {
return $return;
}
if ($validates) {
if ($transactionBegun) {
return $db->commit() !== false;
}

return true;
}
return true;
}

$db->rollback();
return false;
if ($transactionBegun) {
$db->rollback();
}
return false;
} catch (Exception $e) {
if ($transactionBegun) {
$db->rollback();
}
throw $e;
}
}

/**
Expand Down

0 comments on commit 799500c

Please sign in to comment.