From a490e249fa76b1076ab5ee255636f1f533560842 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Mon, 14 Dec 2009 23:21:26 -0500 Subject: [PATCH] Adding test for validation of with models canceling a save. Adding initial implementation of Model::__validateWithModel(). Correcting error in previous commit. Adding tests for saveAll and validating habtm with models. --- cake/libs/model/model.php | 42 ++++++++- .../libs/model/model_validation.test.php | 94 +++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/cake/libs/model/model.php b/cake/libs/model/model.php index 706c4b5eb..7e771a075 100644 --- a/cake/libs/model/model.php +++ b/cake/libs/model/model.php @@ -2331,7 +2331,8 @@ class Model extends Overloadable { return call_user_func_array(array(&$db, 'query'), $params); } /** - * Returns true if all fields pass validation. + * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations + * that use the 'with' key as well. Since __saveMulti is incapable of exiting a save operation. * * Will validate the currently set data. Use Model::set() or Model::create() to set the active data. * @@ -2342,13 +2343,16 @@ class Model extends Overloadable { */ function validates($options = array()) { $errors = $this->invalidFields($options); + if (empty($errors) && $errors !== false) { + $errors = $this->__validateWithModels($options); + } if (is_array($errors)) { return count($errors) === 0; } return $errors; } /** - * Returns an array of fields that have failed validation. + * Returns an array of fields that have failed validation. On the current model. * * @param string $options An optional array of custom options to be made available in the beforeValidate callback * @return array Array of invalid fields @@ -2498,6 +2502,40 @@ class Model extends Overloadable { $this->validate = $_validate; return $this->validationErrors; } +/** + * Runs validation for hasAndBelongsToMany associations that have 'with' keys + * set. And data in the set() data set. + * + * @param array $options Array of options to use on Valdation of with models + * @return boolean Failure of validation on with models. + * @access private + * @see Model::validates() + */ + function __validateWithModels($options) { + $valid = true; + foreach ($this->data as $assoc => $data) { + if (isset($this->hasAndBelongsToMany[$assoc]) && !empty($this->hasAndBelongsToMany[$assoc]['with'])) { + list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); + $newData = array(); + foreach ((array)$data as $row) { + if (isset($row[$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + if (empty($newData)) { + continue; + } + foreach ($newData as $data) { + $data[$this->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->id; + $this->{$join}->create($data); + $valid = ($valid && $this->{$join}->validates($options)); + } + } + } + return $valid; + } /** * Marks a field as invalid, optionally setting the name of validation * rule (in case of multiple validation for field) that was broken. diff --git a/cake/tests/cases/libs/model/model_validation.test.php b/cake/tests/cases/libs/model/model_validation.test.php index eb0b1de8e..a35e4898d 100644 --- a/cake/tests/cases/libs/model/model_validation.test.php +++ b/cake/tests/cases/libs/model/model_validation.test.php @@ -121,6 +121,100 @@ class ModelValidationTest extends BaseModelTest { $this->assertEqual($TestModel->validate, $validate); } +/** + * test that validates() checks all the 'with' associations as well for validation + * as this can cause partial/wrong data insertion. + * + * @return void + */ + function testValidatesWithAssociations() { + $data = array( + 'Something' => array( + 'id' => 5, + 'title' => 'Extra Fields', + 'body' => 'Extra Fields Body', + 'published' => '1' + ), + 'SomethingElse' => array( + array('something_else_id' => 1, 'doomed' => '') + ) + ); + $Something =& new Something(); + $JoinThing =& $Something->JoinThing; + + $JoinThing->validate = array('doomed' => array('rule' => 'notEmpty')); + + $expectedError = array('doomed' => 'This field cannot be left blank'); + + $Something->create(); + $result = $Something->save($data); + $this->assertFalse($result, 'Save occured even when with models failed. %s'); + $this->assertEqual($JoinThing->validationErrors, $expectedError); + $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); + $this->assertIdentical($count, 0); + + $data = array( + 'Something' => array( + 'id' => 5, + 'title' => 'Extra Fields', + 'body' => 'Extra Fields Body', + 'published' => '1' + ), + 'SomethingElse' => array( + array('something_else_id' => 1, 'doomed' => 1), + array('something_else_id' => 1, 'doomed' => '') + ) + ); + $Something->create(); + $result = $Something->save($data); + $this->assertFalse($result, 'Save occured even when with models failed. %s'); + + $joinRecords = $JoinThing->find('count', array( + 'conditions' => array('JoinThing.something_id' => $data['Something']['id']) + )); + $this->assertEqual($joinRecords, 0, 'Records were saved on the join table. %s'); + } +/** + * test that saveAll and with models with validation interact well + * + * @return void + */ + function testValidatesWithModelsAndSaveAll() { + $data = array( + 'Something' => array( + 'id' => 5, + 'title' => 'Extra Fields', + 'body' => 'Extra Fields Body', + 'published' => '1' + ), + 'SomethingElse' => array( + array('something_else_id' => 1, 'doomed' => '') + ) + ); + $Something =& new Something(); + $JoinThing =& $Something->JoinThing; + + $JoinThing->validate = array('doomed' => array('rule' => 'notEmpty')); + $expectedError = array('doomed' => 'This field cannot be left blank'); + + $Something->create(); + $result = $Something->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $this->assertEqual($JoinThing->validationErrors, $expectedError); + + $Something->create(); + $result = $Something->saveAll($data, array('validate' => 'first')); + $this->assertFalse($result); + $this->assertEqual($JoinThing->validationErrors, $expectedError); + + $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); + $this->assertIdentical($count, 0); + + $joinRecords = $JoinThing->find('count', array( + 'conditions' => array('JoinThing.something_id' => $data['Something']['id']) + )); + $this->assertEqual($joinRecords, 0, 'Records were saved on the join table. %s'); + } } ?> \ No newline at end of file