Permalink
Browse files

Added option['deep'] to saveAll, to save unlimited levels associated …

…data

Squashed commit of the following:

commit 45caa54
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 22:29:33 2012 +0100

    update docblocks for deep save

commit 6f3c3b9
Merge: 1d32698 1dd0ff1
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 18:17:34 2012 +0100

    Merge branch '2.1' into 2.1-saveAllTheThings

commit 1d32698
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 18:16:57 2012 +0100

    Revert "adding info in docblock about associated model fieldList"

    This reverts commit 7cc10a2.

commit 7cc10a2
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 18:05:18 2012 +0100

    adding info in docblock about associated model fieldList

commit db2ad27
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 17:51:44 2012 +0100

    add tests for deep saveAll respecting fieldList option

commit 14123fc
Merge: cfdf25d 2afb05b
Author: Ceeram <c33ram@gmail.com>
Date:   Sun Feb 12 16:51:26 2012 +0100

    Merge branch '2.1' into 2.1-saveAllTheThings

commit cfdf25d
Author: Ceeram <c33ram@gmail.com>
Date:   Tue Feb 7 13:54:59 2012 +0100

    Make saveAllTheThings fully BC, use $options['deep'] = true, to save infinite recursive data

commit 6e8c438
Merge: 203c7e1 95aa7e3
Author: Ceeram <c33ram@gmail.com>
Date:   Wed Jan 25 14:22:26 2012 +0100

    Merge branch '2.1' into 2.1-saveAllTheThings

commit 203c7e1
Author: Ceeram <c33ram@gmail.com>
Date:   Wed Jan 25 14:19:25 2012 +0100

    validate all the things as well

commit d920d00
Merge: d648f6a 4f1be12
Author: Ceeram <c33ram@gmail.com>
Date:   Mon Jan 23 18:22:12 2012 +0100

    Merge branch '2.1' into 2.1-saveAllTheThings

commit d648f6a
Author: Ceeram <c33ram@gmail.com>
Date:   Fri Dec 9 15:11:52 2011 +0100

    making saveAll, saveMany and saveAssociated not limited to only save directly related models
  • Loading branch information...
1 parent 1dd0ff1 commit bc07ba3839db17d6ffd1a6c19c0f53a209af49a1 @ceeram ceeram committed Feb 13, 2012
Showing with 987 additions and 75 deletions.
  1. +74 −24 lib/Cake/Model/Model.php
  2. +906 −51 lib/Cake/Test/Case/Model/ModelWriteTest.php
  3. +7 −0 lib/Cake/Test/Case/Model/models.php
View
@@ -1974,6 +1974,7 @@ protected function _prepareUpdateFields($data) {
* 'AssociatedModel' => array('field', 'otherfield')
* )
* }}}
+ * - deep: see saveMany/saveAssociated
*
* @param array $data Record data to save. This can be either a numerically-indexed array (for saving multiple
* records of the same type), or an array indexed by association name.
@@ -1993,11 +1994,7 @@ public function saveAll($data = null, $options = array()) {
return $this->saveMany($data, $options);
}
if ($options['validate'] === 'only') {
- $validatesAssoc = $this->validateAssociated($data, $options);
- if (isset($this->validationErrors[$this->alias]) && $this->validationErrors[$this->alias] === false) {
- return false;
- }
- return $validatesAssoc;
+ return $this->validateAssociated($data, $options);
}
return $this->saveAssociated($data, $options);
}
@@ -2012,6 +2009,7 @@ public function saveAll($data = null, $options = array()) {
* - atomic: If true (default), will attempt to save all records in a single transaction.
* Should be set to false if database/table does not support transactions.
* - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, all associated data will be saved as well.
*
* @param array $data Record data to save. This should be a numerically-indexed array
* @param array $options Options to use when saving record data, See $options above.
@@ -2025,7 +2023,7 @@ public function saveMany($data = null, $options = array()) {
$data = $this->data;
}
- $options = array_merge(array('validate' => 'first', 'atomic' => true), $options);
+ $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options);
$this->validationErrors = $validationErrors = array();
if (empty($data) && $options['validate'] !== false) {
@@ -2046,12 +2044,21 @@ public function saveMany($data = null, $options = array()) {
}
$return = array();
foreach ($data as $key => $record) {
- $validates = ($this->create(null) !== null && $this->save($record, $options));
+ $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;
}
if (!$options['atomic']) {
- $return[] = $validates;
+ $return[$key] = $validates;
} elseif (!$validates) {
break;
}
@@ -2079,6 +2086,7 @@ public function saveMany($data = null, $options = array()) {
*
* - atomic: If true (default), returns boolean. If false returns array.
* - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, all associated data will be validated as well.
*
* @param array $data Record data to validate. This should be a numerically-indexed array
* @param array $options Options to use when validating record data (see above), See also $options of validates().
@@ -2088,14 +2096,21 @@ public function saveMany($data = null, $options = array()) {
* depending on whether each record validated successfully.
*/
public function validateMany($data, $options = array()) {
- $options = array_merge(array('atomic' => true), $options);
+ $options = array_merge(array('atomic' => true, 'deep' => false), $options);
$this->validationErrors = $validationErrors = $return = array();
foreach ($data as $key => $record) {
- $validates = $this->create($record) && $this->validates($options);
- if (!$validates) {
+ if ($options['deep']) {
+ $validates = $this->validateAssociated($record, $options);
+ } else {
+ $validates = $this->create($record) && $this->validates($options);
+ }
+ if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) {
$validationErrors[$key] = $this->validationErrors;
+ $validates = false;
+ } else {
+ $validates = true;
}
- $return[] = $validates;
+ $return[$key] = $validates;
}
$this->validationErrors = $validationErrors;
if (!$options['atomic']) {
@@ -2124,6 +2139,7 @@ public function validateMany($data, $options = array()) {
* 'AssociatedModel' => array('field', 'otherfield')
* )
* }}}
+ * - deep: If set to true, not only directly associated data is saved, but deeper nested associated data as well.
*
* @param array $data Record data to save. This should be an array indexed by association name.
* @param array $options Options to use when saving record data, See $options above.
@@ -2137,7 +2153,7 @@ public function saveAssociated($data = null, $options = array()) {
$data = $this->data;
}
- $options = array_merge(array('validate' => 'first', 'atomic' => true), $options);
+ $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options);
$this->validationErrors = $validationErrors = array();
if (empty($data) && $options['validate'] !== false) {
@@ -2160,13 +2176,26 @@ public function saveAssociated($data = null, $options = array()) {
$validates = true;
foreach ($data as $association => $values) {
if (isset($associations[$association]) && $associations[$association] === 'belongsTo') {
- if ($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options)) {
- $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
+ $validates = $this->{$association}->create(null) !== null;
+ $saved = false;
+ if ($validates) {
+ if ($options['deep']) {
+ $saved = $this->{$association}->saveAssociated($values, array_merge($options, array('atomic' => false)));
+ } else {
+ $saved = $this->{$association}->save($values, array_merge($options, array('atomic' => false)));
+ }
+ $validates = ($saved === true || (is_array($saved) && !in_array(false, $saved, true)));
+ }
+ if ($validates) {
+ if (!empty($data[$this->alias])) {
+ $data[$this->alias][$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
+ } else {
+ $data[$this->belongsTo[$association]['foreignKey']] = $this->{$association}->id;
+ }
} else {
$validationErrors[$association] = $this->{$association}->validationErrors;
- $validates = false;
}
- $return[$association][] = $validates;
+ $return[$association] = $validates;
}
}
if ($validates && !($this->create(null) !== null && $this->save($data, $options))) {
@@ -2184,11 +2213,20 @@ public function saveAssociated($data = null, $options = array()) {
switch ($type) {
case 'hasOne':
$values[$this->{$type}[$association]['foreignKey']] = $this->id;
- if (!($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options))) {
+ $validates = $this->{$association}->create(null) !== null;
+ $saved = false;
+ if ($validates) {
+ if ($options['deep']) {
+ $saved = $this->{$association}->saveAssociated($values, array_merge($options, array('atomic' => false)));
+ } else {
+ $saved = $this->{$association}->save($values, $options);
+ }
+ }
+ $validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
+ if (!$validates) {
$validationErrors[$association] = $this->{$association}->validationErrors;
- $validates = false;
}
- $return[$association][] = $validates;
+ $return[$association] = $validates;
break;
case 'hasMany':
foreach ($values as $i => $value) {
@@ -2231,6 +2269,7 @@ public function saveAssociated($data = null, $options = array()) {
*
* - atomic: If true (default), returns boolean. If false returns array.
* - fieldList: Equivalent to the $fieldList parameter in Model::save()
+ * - deep: If set to true, not only directly associated data , but deeper nested associated data is validated as well.
*
* @param array $data Record data to validate. This should be an array indexed by association name.
* @param array $options Options to use when validating record data (see above), See also $options of validates().
@@ -2239,7 +2278,7 @@ public function saveAssociated($data = null, $options = array()) {
* depending on whether each record validated successfully.
*/
public function validateAssociated($data, $options = array()) {
- $options = array_merge(array('atomic' => true), $options);
+ $options = array_merge(array('atomic' => true, 'deep' => false), $options);
$this->validationErrors = $validationErrors = $return = array();
if (!($this->create($data) && $this->validates($options))) {
$validationErrors[$this->alias] = $this->validationErrors;
@@ -2248,12 +2287,23 @@ public function validateAssociated($data, $options = array()) {
$return[$this->alias] = true;
}
$associations = $this->getAssociated();
- $validates = true;
foreach ($data as $association => $values) {
+ $validates = true;
if (isset($associations[$association])) {
if (in_array($associations[$association], array('belongsTo', 'hasOne'))) {
- $validates = $this->{$association}->create($values) && $this->{$association}->validates($options);
- $return[$association][] = $validates;
+ if ($options['deep']) {
+ $validates = $this->{$association}->validateAssociated($values, $options);
+ } else {
+ $validates = $this->{$association}->create($values) !== null && $this->{$association}->validates($options);
+ }
+ if (is_array($validates)) {
+ if (in_array(false, $validates, true)) {
+ $validates = false;
+ } else {
+ $validates = true;
+ }
+ }
+ $return[$association] = $validates;
} elseif ($associations[$association] === 'hasMany') {
$validates = $this->{$association}->validateMany($values, $options);
$return[$association] = $validates;
Oops, something went wrong.

1 comment on commit bc07ba3

Member

jippi commented on bc07ba3 Feb 15, 2012

Finally! Really been looking forward to this enhancement ! :D

Please sign in to comment.