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

Squashed commit of the following:

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

    update docblocks for deep save

commit 6f3c3b9abf12e394262b0a233188a52095f64b50
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 1d32698640fa1a3c1d606eeaf4740637ff8a1991
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 7cc10a2b5afc1007c388a6da449781dc351d50cd.

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

    adding info in docblock about associated model fieldList

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

    add tests for deep saveAll respecting fieldList option

commit 14123fccfc43fac1b4cddeea8350dca8fb9c9821
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 cfdf25d4b587c6f1a8cd6ec2bc46dc21fa3c6704
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 6e8c4380c37a31efc2a37a5ab6438db26d293eb3
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 203c7e1d9870e282ec7098297d47e49132904401
Author: Ceeram <c33ram@gmail.com>
Date:   Wed Jan 25 14:19:25 2012 +0100

    validate all the things as well

commit d920d0000fcab80fc48a29b45dbba147a2eed27c
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 d648f6a90419e0fa27ed1d9e9feb51c37fbf0397
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
This commit is contained in:
Ceeram 2012-02-13 01:00:28 +01:00
parent 1dd0ff1701
commit bc07ba3839
3 changed files with 947 additions and 35 deletions

View file

@ -1974,6 +1974,7 @@ class Model extends Object implements CakeEventListener {
* '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 @@ class Model extends Object implements CakeEventListener {
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 @@ class Model extends Object implements CakeEventListener {
* - 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 @@ class Model extends Object implements CakeEventListener {
$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 @@ class Model extends Object implements CakeEventListener {
}
$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 @@ class Model extends Object implements CakeEventListener {
*
* - 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 @@ class Model extends Object implements CakeEventListener {
* 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) {
$validationErrors[$key] = $this->validationErrors;
if ($options['deep']) {
$validates = $this->validateAssociated($record, $options);
} else {
$validates = $this->create($record) && $this->validates($options);
}
$return[] = $validates;
if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) {
$validationErrors[$key] = $this->validationErrors;
$validates = false;
} else {
$validates = true;
}
$return[$key] = $validates;
}
$this->validationErrors = $validationErrors;
if (!$options['atomic']) {
@ -2124,6 +2139,7 @@ class Model extends Object implements CakeEventListener {
* '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 @@ class Model extends Object implements CakeEventListener {
$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 @@ class Model extends Object implements CakeEventListener {
$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 @@ class Model extends Object implements CakeEventListener {
switch ($type) {
case 'hasOne':
$values[$this->{$type}[$association]['foreignKey']] = $this->id;
if (!($this->{$association}->create(null) !== null && $this->{$association}->save($values, $options))) {
$validationErrors[$association] = $this->{$association}->validationErrors;
$validates = false;
$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);
}
}
$return[$association][] = $validates;
$validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, $saved, true))));
if (!$validates) {
$validationErrors[$association] = $this->{$association}->validationErrors;
}
$return[$association] = $validates;
break;
case 'hasMany':
foreach ($values as $i => $value) {
@ -2231,6 +2269,7 @@ class Model extends Object implements CakeEventListener {
*
* - 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 @@ class Model extends Object implements CakeEventListener {
* 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 @@ class Model extends Object implements CakeEventListener {
$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;

View file

@ -2973,7 +2973,7 @@ class ModelWriteTest extends BaseModelTest {
* @return void
*/
public function testSaveAllAtomic() {
$this->loadFixtures('Article', 'User');
$this->loadFixtures('Article', 'User', 'Comment');
$TestModel = new Article();
$result = $TestModel->saveAll(array(
@ -3041,6 +3041,775 @@ class ModelWriteTest extends BaseModelTest {
$this->assertSame($result, array('Article' => true, 'Comment' => array(true, true)));
}
/**
* testSaveAllDeepAssociated method
*
* @return void
*/
public function testSaveAllDeepAssociated() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => 'newuser', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
), array('deep' => true));
$this->assertTrue($result);
$result = $TestModel->findById(2);
$expected = array(
'First Comment for Second Article',
'Second Comment for Second Article',
'First new comment',
'Second new comment'
);
$this->assertEquals($expected, Set::extract($result['Comment'], '{n}.comment'));
$result = $TestModel->Comment->User->field('id', array('user' => 'newuser', 'password' => 'newuserpass'));
$this->assertEquals(5, $result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => 'deepsaved'))
)
), array('deep' => true));
$this->assertTrue($result);
$result = $TestModel->findById(2);
$expected = array(
'First Comment for Second Article',
'Second Comment for Second Article',
'First new comment',
'Second new comment',
'Third new comment',
'Fourth new comment'
);
$this->assertEquals($expected, Set::extract($result['Comment'], '{n}.comment'));
$result = $TestModel->Comment->Attachment->field('id', array('attachment' => 'deepsaved'));
$this->assertEquals(2, $result);
$data = array(
'Attachment' => array(
'attachment' => 'deepsave insert',
),
'Comment' => array(
'comment' => 'First comment deepsave insert',
'published' => 'Y',
'user_id' => 5,
'Article' => array(
'title' => 'First Article deepsave insert',
'body' => 'First Article Body deepsave insert',
'User' => array(
'user' => '',
'password' => 'magic'
),
),
)
);
$TestModel->Comment->Attachment->create();
$result = $TestModel->Comment->Attachment->saveAll($data, array('deep' => true));
$this->assertFalse($result);
$expected = array('User' => array('user' => array('This field cannot be left blank')));
$this->assertEquals($expected, $TestModel->validationErrors);
$data['Comment']['Article']['User']['user'] = 'deepsave';
$TestModel->Comment->Attachment->create();
$result = $TestModel->Comment->Attachment->saveAll($data, array('deep' => true));
$this->assertTrue($result);
$result = $TestModel->Comment->Attachment->findById($TestModel->Comment->Attachment->id);
$expected = array(
'Attachment' => array(
'id' => '3',
'comment_id' => '11',
'attachment' => 'deepsave insert',
),
'Comment' => array(
'id' => '11',
'article_id' => '4',
'user_id' => '5',
'comment' => 'First comment deepsave insert',
'published' => 'Y',
)
);
unset($result['Attachment']['created'], $result['Attachment']['updated']);
$this->assertEquals($expected['Attachment'], $result['Attachment']);
unset($result['Comment']['created'], $result['Comment']['updated']);
$this->assertEquals($result['Comment'], $expected['Comment']);
$result = $TestModel->findById($result['Comment']['article_id']);
$expected = array(
'Article' => array(
'id' => '4',
'user_id' => '6',
'title' => 'First Article deepsave insert',
'body' => 'First Article Body deepsave insert',
'published' => 'N',
),
'User' => array(
'id' => '6',
'user' => 'deepsave',
'password' => 'magic',
),
'Comment' => array(
array(
'id' => '11',
'article_id' => '4',
'user_id' => '5',
'comment' => 'First comment deepsave insert',
'published' => 'Y',
)
)
);
unset(
$result['Article']['created'], $result['Article']['updated'],
$result['User']['created'], $result['User']['updated'],
$result['Comment'][0]['created'], $result['Comment'][0]['updated']
);
$this->assertEquals($result, $expected);
}
/**
* testSaveAllDeepMany
* tests the validate methods with deeper recursive data
*
* @return void
*/
public function testSaveAllDeepMany() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$data = array(
array(
'id' => 1, 'body' => '',
'Comment' => array(
array('comment' => '', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'manysaved')),
array('comment' => 'Second comment deepsaved article 1', 'published' => 'Y', 'user_id' => 2)
)
),
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First comment deepsaved article 2', 'published' => 'Y', 'User' => array('user' => 'savemore', 'password' => '')),
array('comment' => '', 'published' => 'Y', 'user_id' => 2)
)
)
);
$TestModel->Comment->validate['comment'] = 'notEmpty';
$result = $TestModel->saveAll($data, array('deep' => true));
$this->assertFalse($result);
$expected = array(
0 => array(
'body' => array('This field cannot be left blank')
),
1 => array(
'Comment' => array(
0 => array(
'User' => array(
'password' => array('This field cannot be left blank')
)
),
1 => array(
'comment' => array('This field cannot be left blank')
)
)
)
);
$result = $TestModel->validationErrors;
$this->assertSame($expected, $result);
$data = array(
array(
'Article' => array('id' => 1),
'Comment' => array(
array('comment' => 'First comment deepsaved article 1', 'published' => 'Y', 'User' => array('user' => 'savemany', 'password' => 'manysaved')),
array('comment' => 'Second comment deepsaved article 1', 'published' => 'Y', 'user_id' => 2)
)
),
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First comment deepsaved article 2', 'published' => 'Y', 'User' => array('user' => 'savemore', 'password' => 'moresaved')),
array('comment' => 'Second comment deepsaved article 2', 'published' => 'Y', 'user_id' => 2)
)
)
);
$result = $TestModel->saveAll($data, array('deep' => true));
$this->assertTrue($result);
}
/**
* testSaveAllDeepValidateOnly
* tests the validate methods with deeper recursive data
*
* @return void
*/
public function testSaveAllDeepValidateOnly() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$TestModel->Comment->Attachment->validate['attachment'] = 'notEmpty';
$TestModel->Comment->validate['comment'] = 'notEmpty';
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => 'newuser', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'deep' => true)
);
$this->assertTrue($result);
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'deep' => true)
);
$this->assertFalse($result);
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => 'newuser', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => true)
);
$expected = array(
'Article' => true,
'Comment' => array(
true,
true
)
);
$this->assertSame($expected, $result);
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => true)
);
$expected = array(
'Article' => true,
'Comment' => array(
false,
true
)
);
$this->assertSame($expected, $result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => 'deepsaved'))
)
),
array('validate' => 'only', 'deep' => true)
);
$this->assertTrue($result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => ''))
)
),
array('validate' => 'only', 'deep' => true)
);
$this->assertFalse($result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => 'deepsave'))
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => true)
);
$expected = array(
'Article' => true,
'Comment' => array(
true,
true
)
);
$this->assertSame($expected, $result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => ''))
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => true)
);
$expected = array(
'Article' => true,
'Comment' => array(
true,
false
)
);
$this->assertSame($expected, $result);
$expected = array(
'Comment' => array(
1 => array(
'Attachment' => array(
'attachment' => array('This field cannot be left blank')
)
)
)
);
$result = $TestModel->validationErrors;
$this->assertSame($expected, $result);
$data = array(
'Attachment' => array(
'attachment' => 'deepsave insert',
),
'Comment' => array(
'comment' => 'First comment deepsave insert',
'published' => 'Y',
'user_id' => 5,
'Article' => array(
'title' => 'First Article deepsave insert',
'body' => 'First Article Body deepsave insert',
'User' => array(
'user' => 'deepsave',
'password' => 'magic'
),
),
)
);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => true));
$this->assertTrue($result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true));
$expected = array(
'Attachment' => true,
'Comment' => true
);
$this->assertSame($expected, $result);
$data = array(
'Attachment' => array(
'attachment' => 'deepsave insert',
),
'Comment' => array(
'comment' => 'First comment deepsave insert',
'published' => 'Y',
'user_id' => 5,
'Article' => array(
'title' => 'First Article deepsave insert',
'body' => 'First Article Body deepsave insert',
'User' => array(
'user' => '',
'password' => 'magic'
),
),
)
);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => true));
$this->assertFalse($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array(
'Comment' => array(
'Article' => array(
'User' => array(
'user' => array('This field cannot be left blank')
)
)
)
);
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true));
$expected = array(
'Attachment' => true,
'Comment' => false
);
$this->assertEquals($expected, $result);
$data['Comment']['Article']['body'] = '';
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => true));
$this->assertFalse($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array(
'Comment' => array(
'Article' => array(
'body' => array('This field cannot be left blank')
)
)
);
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true));
$expected = array(
'Attachment' => true,
'Comment' => false
);
$this->assertEquals($expected, $result);
$data['Comment']['comment'] = '';
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => true));
$this->assertFalse($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array(
'Comment' => array(
'comment' => array('This field cannot be left blank')
)
);
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true));
$expected = array(
'Attachment' => true,
'Comment' => false
);
$this->assertEquals($expected, $result);
$data['Attachment']['attachment'] = '';
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => true));
$this->assertFalse($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array('attachment' => array('This field cannot be left blank'));
$this->assertSame($expected, $result);
$result = $TestModel->Comment->validationErrors;
$expected = array('comment' => array('This field cannot be left blank'));
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true));
$expected = array(
'Attachment' => false,
'Comment' => false
);
$this->assertEquals($expected, $result);
}
/**
* testSaveAllNotDeepAssociated method
* test that only directly associated data gets saved
*
* @return void
*/
public function testSaveAllNotDeepAssociated() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => 'newuser', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
), array('deep' => false));
$this->assertTrue($result);
$result = $TestModel->Comment->User->field('id', array('user' => 'newuser', 'password' => 'newuserpass'));
$this->assertFalse($result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 4),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => 'deepsaved'))
)
), array('deep' => false));
$this->assertTrue($result);
$result = $TestModel->Comment->Attachment->field('id', array('attachment' => 'deepsaved'));
$this->assertFalse($result);
$data = array(
'Attachment' => array(
'attachment' => 'deepsave insert',
),
'Comment' => array(
'comment' => 'First comment deepsave insert',
'published' => 'Y',
'user_id' => 4,
'Article' => array(
'title' => 'First Article deepsave insert',
'body' => 'First Article Body deepsave insert',
'User' => array(
'user' => 'deepsave',
'password' => 'magic'
),
),
)
);
$expected = $TestModel->User->find('count');
$TestModel->Comment->Attachment->create();
$result = $TestModel->Comment->Attachment->saveAll($data, array('deep' => false));
$this->assertTrue($result);
$result = $TestModel->User->find('count');
$this->assertEquals($expected, $result);
$result = $TestModel->Comment->Attachment->findById($TestModel->Comment->Attachment->id);
$expected = array(
'Attachment' => array(
'id' => '2',
'comment_id' => '11',
'attachment' => 'deepsave insert',
),
'Comment' => array(
'id' => '11',
'article_id' => '0',
'user_id' => '4',
'comment' => 'First comment deepsave insert',
'published' => 'Y',
)
);
unset($result['Attachment']['created'], $result['Attachment']['updated']);
$this->assertEquals($expected['Attachment'], $result['Attachment']);
unset($result['Comment']['created'], $result['Comment']['updated']);
$this->assertEquals($expected['Comment'], $result['Comment']);
}
/**
* testSaveAllNotDeepMany
* tests the save methods to not save deeper recursive data
*
* @return void
*/
public function testSaveAllNotDeepMany() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$data = array(
array(
'id' => 1, 'body' => '',
'Comment' => array(
array('comment' => '', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'manysaved')),
array('comment' => 'Second comment deepsaved article 1', 'published' => 'Y', 'user_id' => 2)
)
),
array(
'Article' => array('id' => 2),
'Comment' => array(
array('comment' => 'First comment deepsaved article 2', 'published' => 'Y', 'User' => array('user' => 'savemore', 'password' => '')),
array('comment' => '', 'published' => 'Y', 'user_id' => 2)
)
)
);
$TestModel->Comment->validate['comment'] = 'notEmpty';
$result = $TestModel->saveAll($data, array('deep' => false));
$this->assertFalse($result);
$expected = array(
0 => array(
'body' => array('This field cannot be left blank')
)
);
$result = $TestModel->validationErrors;
$this->assertSame($expected, $result);
$data = array(
array(
'Article' => array('id' => 1, 'body' => 'Ignore invalid comment'),
'Comment' => array(
array('comment' => '', 'published' => 'Y', 'user_id' => 2)
)
),
array(
'Article' => array('id' => 2, 'body' => 'Same here'),
'Comment' => array(
array('comment' => '', 'published' => 'Y', 'user_id' => 2)
)
)
);
$result = $TestModel->saveAll($data, array('deep' => false));
$this->assertTrue($result);
}
/**
* testSaveAllNotDeepValidateOnly
* tests the validate methods to not validate deeper recursive data
*
* @return void
*/
public function testSaveAllNotDeepValidateOnly() {
$this->loadFixtures('Article', 'Comment', 'User', 'Attachment');
$TestModel = new Article();
$TestModel->hasMany['Comment']['order'] = array('Comment.created' => 'ASC');
$TestModel->hasAndBelongsToMany = array();
$TestModel->Comment->Attachment->validate['attachment'] = 'notEmpty';
$TestModel->Comment->validate['comment'] = 'notEmpty';
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2, 'body' => ''),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'deep' => false)
);
$this->assertFalse($result);
$expected = array('body' => array('This field cannot be left blank'));
$result = $TestModel->validationErrors;
$this->assertSame($expected, $result);
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2, 'body' => 'Ignore invalid user data'),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'deep' => false)
);
$this->assertTrue($result);
$result = $TestModel->saveAll(
array(
'Article' => array('id' => 2, 'body' => 'Ignore invalid user data'),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'User' => array('user' => '', 'password' => 'newuserpass')),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2)
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => false)
);
$expected = array(
'Article' => true,
'Comment' => array(
true,
true
)
);
$this->assertSame($expected, $result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2, 'body' => 'Ignore invalid attachment data'),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => ''))
)
),
array('validate' => 'only', 'deep' => false)
);
$this->assertTrue($result);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2, 'body' => 'Ignore invalid attachment data'),
'Comment' => array(
array('comment' => 'Third new comment', 'published' => 'Y', 'user_id' => 5),
array('comment' => 'Fourth new comment', 'published' => 'Y', 'user_id' => 2, 'Attachment' => array('attachment' => ''))
)
),
array('validate' => 'only', 'atomic' => false, 'deep' => false)
);
$expected = array(
'Article' => true,
'Comment' => array(
true,
true
)
);
$this->assertSame($expected, $result);
$expected = array();
$result = $TestModel->validationErrors;
$this->assertSame($expected, $result);
$data = array(
'Attachment' => array(
'attachment' => 'deepsave insert',
),
'Comment' => array(
'comment' => 'First comment deepsave insert',
'published' => 'Y',
'user_id' => 5,
'Article' => array(
'title' => 'First Article deepsave insert ignored',
'body' => 'First Article Body deepsave insert',
'User' => array(
'user' => '',
'password' => 'magic'
),
),
)
);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => false));
$this->assertTrue($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array();
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false));
$expected = array(
'Attachment' => true,
'Comment' => true
);
$this->assertEquals($expected, $result);
$data['Comment']['Article']['body'] = '';
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'deep' => false));
$this->assertTrue($result);
$result = $TestModel->Comment->Attachment->validationErrors;
$expected = array();
$this->assertSame($expected, $result);
$result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false));
$expected = array(
'Attachment' => true,
'Comment' => true
);
$this->assertEquals($expected, $result);
}
/**
* testSaveAllHasMany method
*
@ -3212,7 +3981,7 @@ class ModelWriteTest extends BaseModelTest {
$db->expects($this->once())->method('rollback');
$db->expects($this->any())->method('describe')
->will($this->returnValue(array(
'id' => array('type' => 'integer'),
'id' => array('type' => 'integer', 'length' => 11),
'title' => array('type' => 'string'),
'body' => array('type' => 'text'),
'published' => array('type' => 'string')
@ -4541,7 +5310,7 @@ class ModelWriteTest extends BaseModelTest {
$db->expects($this->once())->method('rollback');
$db->expects($this->any())->method('describe')
->will($this->returnValue(array(
'id' => array('type' => 'integer'),
'id' => array('type' => 'integer', 'length' => 11),
'title' => array('type' => 'string'),
'body' => array('type' => 'text'),
'published' => array('type' => 'string')
@ -5512,11 +6281,11 @@ class ModelWriteTest extends BaseModelTest {
}
/**
* validateSaveAllFieldListBelongsTo
* testSaveAllFieldListValidateBelongsTo
*
* @return void
*/
public function validateSaveAllFieldListBelongsTo() {
public function testSaveAllFieldListValidateBelongsTo() {
$this->loadFixtures('Post', 'Author', 'Comment', 'Attachment');
$TestModel = new Post();
@ -5543,8 +6312,7 @@ class ModelWriteTest extends BaseModelTest {
$result = $TestModel->find('all');
$expected = array(
'Post' =>
array (
'Post' => array (
'id' => '4',
'author_id' => '5',
'title' => 'Post without body',
@ -5553,8 +6321,7 @@ class ModelWriteTest extends BaseModelTest {
'created' => $ts,
'updated' => $ts,
),
'Author' =>
array (
'Author' => array (
'id' => '5',
'user' => 'bob',
'password' => NULL,
@ -5683,4 +6450,92 @@ class ModelWriteTest extends BaseModelTest {
$this->assertEmpty($TestModel->validationErrors);
}
/**
* testSaveAllDeepFieldListValidateBelongsTo
*
* @return void
*/
public function testSaveAllDeepFieldListValidateBelongsTo() {
$this->loadFixtures('Post', 'Author', 'Comment', 'Attachment', 'Article', 'User');
$TestModel = new Post();
$TestModel->Author->bindModel(array('hasMany' => array('Comment' => array('foreignKey' => 'user_id'))), false);
$TestModel->recursive = 2;
$result = $TestModel->find('all');
$this->assertCount(3, $result);
$this->assertFalse(isset($result[3]));
$ts = date('Y-m-d H:i:s');
// test belongsTo
$fieldList = array(
'Post' => array('title', 'author_id'),
'Author' => array('user'),
'Comment' => array('comment')
);
$TestModel->saveAll(array(
'Post' => array(
'title' => 'Post without body',
'body' => 'This will not be saved',
),
'Author' => array(
'user' => 'bob',
'test' => 'This will not be saved',
'Comment' => array(
array('id' => 5, 'comment' => 'I am still published', 'published' => 'N'))
)), array('fieldList' => $fieldList, 'deep' => true));
$result = $TestModel->Author->Comment->find('first', array(
'conditions' => array('Comment.id' => 5),
'fields' => array('comment', 'published')
));
$expected = array(
'Comment' => array(
'comment' => 'I am still published',
'published' => 'Y'
)
);
$this->assertEquals($expected, $result);
}
/**
* testSaveAllDeepFieldListHasMany method
*
* return @void
*/
public function testSaveAllDeepFieldListHasMany() {
$this->loadFixtures('Article', 'Comment', 'User');
$TestModel = new Article();
$TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array();
$this->db->truncate($TestModel);
$this->db->truncate(new Comment());
$fieldList = array(
'Article' => array('id'),
'Comment' => array('article_id', 'user_id'),
'User' => array('user')
);
$result = $TestModel->saveAll(array(
'Article' => array('id' => 2, 'title' => 'I will not save'),
'Comment' => array(
array('comment' => 'First new comment', 'published' => 'Y', 'user_id' => 1),
array('comment' => 'Second new comment', 'published' => 'Y', 'user_id' => 2, 'User' => array('user' => 'nopassword', 'password' => 'not saved'))
)
), array('fieldList' => $fieldList, 'deep' => true));
$result = $TestModel->Comment->User->find('first', array(
'conditions' => array('User.user' => 'nopassword'),
'fields' => array('user', 'password')
));
$expected = array(
'User' => array(
'user' => 'nopassword',
'password' => ''
)
);
$this->assertEquals($expected, $result);
}
}

View file

@ -641,6 +641,13 @@ class Attachment extends CakeTestModel {
* @var string 'Attachment'
*/
public $name = 'Attachment';
/**
* belongsTo property
*
* @var array
*/
public $belongsTo = array('Comment');
}
/**