From 324684c14f65f062aeb11957b729c55157b99741 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Thu, 22 Mar 2012 12:18:57 +0100 Subject: [PATCH 01/42] Moving all validation logic into a new ModelValidator object. --- lib/Cake/Controller/Controller.php | 2 +- lib/Cake/Model/Model.php | 379 +++--------- lib/Cake/Model/ModelValidator.php | 543 ++++++++++++++++++ lib/Cake/Model/Validator/CakeField.php | 188 ++++++ lib/Cake/Model/Validator/CakeRule.php | 404 +++++++++++++ .../Test/Case/Controller/ControllerTest.php | 2 +- .../Test/Case/Model/ModelValidationTest.php | 10 +- lib/Cake/Test/Case/Model/ModelWriteTest.php | 42 +- 8 files changed, 1259 insertions(+), 311 deletions(-) create mode 100644 lib/Cake/Model/ModelValidator.php create mode 100644 lib/Cake/Model/Validator/CakeField.php create mode 100644 lib/Cake/Model/Validator/CakeRule.php diff --git a/lib/Cake/Controller/Controller.php b/lib/Cake/Controller/Controller.php index 7b427313e..5c66f0509 100644 --- a/lib/Cake/Controller/Controller.php +++ b/lib/Cake/Controller/Controller.php @@ -528,7 +528,7 @@ class Controller extends Object implements CakeEventListener { } /** - * Merge components, helpers, and uses vars from + * Merge components, helpers, and uses vars from * Controller::$_mergeParent and PluginAppController. * * @return void diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index dfcd14508..af7131c72 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -25,6 +25,7 @@ App::uses('String', 'Utility'); App::uses('Hash', 'Utility'); App::uses('BehaviorCollection', 'Model'); App::uses('ModelBehavior', 'Model'); +App::uses('ModelValidator', 'Model'); App::uses('ConnectionManager', 'Model'); App::uses('Xml', 'Utility'); App::uses('CakeEvent', 'Event'); @@ -618,6 +619,13 @@ class Model extends Object implements CakeEventListener { */ protected $_eventManager = null; +/** + * Instance of the ModelValidator + * + * @var ModelValidator + */ + protected $_validator = null; + /** * Constructor. Binds the model's database table to the object. * @@ -723,6 +731,7 @@ class Model extends Object implements CakeEventListener { $this->_createLinks(); $this->Behaviors->init($this->alias, $this->actsAs); + $this->setValidator(); } /** @@ -969,9 +978,8 @@ class Model extends Object implements CakeEventListener { $value = array(); if (strpos($assoc, '.') !== false) { - list($plugin, $assoc) = pluginSplit($assoc); - $this->{$type}[$assoc] = array('className' => $plugin . '.' . $assoc); - } else { + list($plugin, $assoc) = pluginSplit($assoc, true); + $this->{$type}[$assoc] = array('className' => $plugin . $assoc); } else { $this->{$type}[$assoc] = $value; } } @@ -1445,7 +1453,7 @@ class Model extends Object implements CakeEventListener { $defaults = array(); $this->id = false; $this->data = array(); - $this->validationErrors = array(); + $this->validationErrors = $this->getValidator()->validationErrors = array(); if ($data !== null && $data !== false) { foreach ($this->schema() as $field => $properties) { @@ -2032,7 +2040,7 @@ class Model extends Object implements CakeEventListener { } $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options); - $this->validationErrors = $validationErrors = array(); + $validationErrors = array(); if (empty($data) && $options['validate'] !== false) { $result = $this->save($data, $options); @@ -2108,30 +2116,7 @@ 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, 'deep' => false), $options); - $this->validationErrors = $validationErrors = $return = array(); - foreach ($data as $key => $record) { - 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[$key] = $validates; - } - $this->validationErrors = $validationErrors; - if (!$options['atomic']) { - return $return; - } - if (empty($this->validationErrors)) { - return true; - } - return false; + return $this->getValidator()->validateMany($data, $options); } /** @@ -2166,7 +2151,7 @@ class Model extends Object implements CakeEventListener { } $options = array_merge(array('validate' => 'first', 'atomic' => true, 'deep' => false), $options); - $this->validationErrors = $validationErrors = array(); + $validationErrors = array(); if (empty($data) && $options['validate'] !== false) { $result = $this->save($data, $options); @@ -2306,53 +2291,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, 'deep' => false), $options); - $this->validationErrors = $validationErrors = $return = array(); - if (!($this->create($data) && $this->validates($options))) { - $validationErrors[$this->alias] = $this->validationErrors; - $return[$this->alias] = false; - } else { - $return[$this->alias] = true; - } - $associations = $this->getAssociated(); - foreach ($data as $association => $values) { - $validates = true; - if (isset($associations[$association])) { - if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { - 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; - } - if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $validationErrors[$association] = $this->{$association}->validationErrors; - } - } - } - - $this->validationErrors = $validationErrors; - if (isset($validationErrors[$this->alias])) { - $this->validationErrors = $validationErrors[$this->alias]; - } - if (!$options['atomic']) { - return $return; - } - if ($return[$this->alias] === false || !empty($this->validationErrors)) { - return false; - } - return true; + return $this->getValidator()->validateAssociated($data, $options); } /** @@ -3039,14 +2978,7 @@ class Model extends Object implements CakeEventListener { * @return boolean True if there are no errors */ public 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; + return $this->getValidator()->validates($options); } /** @@ -3057,203 +2989,7 @@ class Model extends Object implements CakeEventListener { * @see Model::validates() */ public function invalidFields($options = array()) { - $event = new CakeEvent('Model.beforeValidate', $this, array($options)); - list($event->break, $event->breakOn) = array(true, false); - $this->getEventManager()->dispatch($event); - if ($event->isStopped()) { - return false; - } - - if (!isset($this->validate) || empty($this->validate)) { - return $this->validationErrors; - } - - $data = $this->data; - $methods = array_map('strtolower', get_class_methods($this)); - $behaviorMethods = array_keys($this->Behaviors->methods()); - - if (isset($data[$this->alias])) { - $data = $data[$this->alias]; - } elseif (!is_array($data)) { - $data = array(); - } - - $exists = null; - - $_validate = $this->validate; - $whitelist = $this->whitelist; - - if (!empty($options['fieldList'])) { - if (!empty($options['fieldList'][$this->alias]) && is_array($options['fieldList'][$this->alias])) { - $whitelist = $options['fieldList'][$this->alias]; - } else { - $whitelist = $options['fieldList']; - } - } - - if (!empty($whitelist)) { - $validate = array(); - foreach ((array)$whitelist as $f) { - if (!empty($this->validate[$f])) { - $validate[$f] = $this->validate[$f]; - } - } - $this->validate = $validate; - } - - $validationDomain = $this->validationDomain; - if (empty($validationDomain)) { - $validationDomain = 'default'; - } - - foreach ($this->validate as $fieldName => $ruleSet) { - if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { - $ruleSet = array($ruleSet); - } - $default = array( - 'allowEmpty' => null, - 'required' => null, - 'rule' => 'blank', - 'last' => true, - 'on' => null - ); - - foreach ($ruleSet as $index => $validator) { - if (!is_array($validator)) { - $validator = array('rule' => $validator); - } - $validator = array_merge($default, $validator); - - if (!empty($validator['on']) || in_array($validator['required'], array('create', 'update'), true)) { - if ($exists === null) { - $exists = $this->exists(); - } - if ($validator['on'] == 'create' && $exists || $validator['on'] == 'update' && !$exists) { - continue; - } - if ($validator['required'] === 'create' && !$exists || $validator['required'] === 'update' && $exists) { - $validator['required'] = true; - } - } - - $valid = true; - $requiredFail = ( - (!isset($data[$fieldName]) && $validator['required'] === true) || - ( - isset($data[$fieldName]) && (empty($data[$fieldName]) && - !is_numeric($data[$fieldName])) && $validator['allowEmpty'] === false - ) - ); - - if (!$requiredFail && array_key_exists($fieldName, $data)) { - if (empty($data[$fieldName]) && $data[$fieldName] != '0' && $validator['allowEmpty'] === true) { - break; - } - if (is_array($validator['rule'])) { - $rule = $validator['rule'][0]; - unset($validator['rule'][0]); - $ruleParams = array_merge(array($data[$fieldName]), array_values($validator['rule'])); - } else { - $rule = $validator['rule']; - $ruleParams = array($data[$fieldName]); - } - - if (in_array(strtolower($rule), $methods)) { - $ruleParams[] = $validator; - $ruleParams[0] = array($fieldName => $ruleParams[0]); - $valid = $this->dispatchMethod($rule, $ruleParams); - } elseif (in_array($rule, $behaviorMethods) || in_array(strtolower($rule), $behaviorMethods)) { - $ruleParams[] = $validator; - $ruleParams[0] = array($fieldName => $ruleParams[0]); - $valid = $this->Behaviors->dispatchMethod($this, $rule, $ruleParams); - } elseif (method_exists('Validation', $rule)) { - $valid = call_user_func_array(array('Validation', $rule), $ruleParams); - } elseif (!is_array($validator['rule'])) { - $valid = preg_match($rule, $data[$fieldName]); - } elseif (Configure::read('debug') > 0) { - trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $rule, $fieldName), E_USER_WARNING); - } - } - - if ($requiredFail || !$valid || (is_string($valid) && strlen($valid) > 0)) { - if (is_string($valid)) { - $message = $valid; - } elseif (isset($validator['message'])) { - $args = null; - if (is_array($validator['message'])) { - $message = $validator['message'][0]; - $args = array_slice($validator['message'], 1); - } else { - $message = $validator['message']; - } - if (is_array($validator['rule']) && $args === null) { - $args = array_slice($ruleSet[$index]['rule'], 1); - } - if (!empty($args)) { - foreach ($args as $k => $arg) { - $args[$k] = __d($validationDomain, $arg); - } - } - $message = __d($validationDomain, $message, $args); - } elseif (is_string($index)) { - if (is_array($validator['rule'])) { - $args = array_slice($ruleSet[$index]['rule'], 1); - $message = __d($validationDomain, $index, $args); - } else { - $message = __d($validationDomain, $index); - } - } elseif (!$requiredFail && is_numeric($index) && count($ruleSet) > 1) { - $message = $index + 1; - } else { - $message = __d('cake_dev', 'This field cannot be left blank'); - } - - $this->invalidate($fieldName, $message); - if ($validator['last']) { - break; - } - } - } - } - $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 Validation of with models - * @return boolean Failure of validation on with models. - * @see Model::validates() - */ - protected function _validateWithModels($options) { - $valid = true; - foreach ($this->hasAndBelongsToMany as $assoc => $association) { - if (empty($association['with']) || !isset($this->data[$assoc])) { - continue; - } - list($join) = $this->joinModel($this->hasAndBelongsToMany[$assoc]['with']); - $data = $this->data[$assoc]; - - $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; + return $this->getValidator()->invalidFields($options); } /** @@ -3266,10 +3002,7 @@ class Model extends Object implements CakeEventListener { * @return void */ public function invalidate($field, $value = true) { - if (!is_array($this->validationErrors)) { - $this->validationErrors = array(); - } - $this->validationErrors[$field][] = $value; + $this->getValidator()->invalidate($field, $value); } /** @@ -3616,4 +3349,78 @@ class Model extends Object implements CakeEventListener { } } +/** + * Creates a ModelValidator instance from Model::validatorClass + * + * @return void + * @throws MissingValidatorException + * @throws InvalidValidatorException + */ + public function setValidator($validator = null) { + if (is_object($validator) && $this->_isValidValidator($validator)) { + $this->_validator = $validator; + return $this; + } + + if (is_null($validator) && is_null($this->validatorClass)) { + $this->validatorClass = ModelValidator::DEFAULT_VALIDATOR; + } elseif (is_string($validator)) { + $this->validatorClass = $validator; + } + + if (!$this->_loadValidator($this->validatorClass)) { + throw new MissingValidatorException(array($this->validatorClass)); + } + + if (!$this->_isValidValidator($this->_validator)) { + $this->_validator = null; + throw new InvalidValidatorException(array($this->validatorClass, ModelValidator::DEFAULT_VALIDATOR)); + } + + return $this; + } + +/** + * Returns the currently set ModelValidator instance + * + * @return ModelValidator + */ + public function getValidator() { + return $this->_validator; + } + +/** + * Tries to load a validator and returns true if the class could be found, false otherwise. + * + * @param string $validatorClass The class to be loaded + * @return boolean True if the class was found, false otherwise + */ + protected function _loadValidator($validatorClass) { + list($plugin, $class) = pluginSplit($validatorClass, true); + unset($validatorClass); + + $location = $plugin . 'Model'; + App::uses($class, $location); + + if (!class_exists($class, true)) { + return false; + } + $this->_validator = new $class($this); + + return true; + } + +/** + * Checks if the passed in validator instance is either an instance or subclass of ModelValidator. + * + * @param $validator + * @return boolean True if the instance is valid, false otherwise + */ + protected function _isValidValidator($validator) { + if (!($validator instanceof ModelValidator) && !is_subclass_of($validator, ModelValidator::DEFAULT_VALIDATOR)) { + return false; + } + return true; + } + } diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php new file mode 100644 index 000000000..8bafa2cbb --- /dev/null +++ b/lib/Cake/Model/ModelValidator.php @@ -0,0 +1,543 @@ +_model = $Model; + } + +/** + * 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. + * + * @param array $options An optional array of custom options to be made available in the beforeValidate callback + * @return boolean True if there are no errors + */ + public function validates($options = array()) { + $this->validationErrors = array(); + $errors = $this->invalidFields($options); + if (empty($errors) && $errors !== false) { + $errors = $this->_validateWithModels($options); + } + if (is_array($errors)) { + return count($errors) === 0; + } + return $errors; + } + +/** + * Validates a single record, as well as all its directly associated records. + * + * #### Options + * + * - 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(). + * @return array|boolean 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 validated successfully. + */ + public function validateAssociated($data, $options = array()) { + $options = array_merge(array('atomic' => true, 'deep' => false), $options); + $this->validationErrors = $this->getModel()->validationErrors = $return = array(); + if (!($this->getModel()->create($data) && $this->validates($options))) { + $this->validationErrors = array($this->getModel()->alias => $this->validationErrors); + $return[$this->getModel()->alias] = false; + } else { + $return[$this->getModel()->alias] = true; + } + $associations = $this->getModel()->getAssociated(); + foreach ($data as $association => $values) { + $validates = true; + if (isset($associations[$association])) { + if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { + if ($options['deep']) { + $validates = $this->getModel()->{$association}->getValidator()->validateAssociated($values, $options); + } else { + $validates = $this->getModel()->{$association}->create($values) !== null && $this->getModel()->{$association}->getValidator()->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->getModel()->{$association}->getValidator()->validateMany($values, $options); + $return[$association] = $validates; + } + if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { + $this->validationErrors[$association] = $this->getModel()->{$association}->getValidator()->validationErrors; + } + } + } + + if (isset($this->validationErrors[$this->getModel()->alias])) { + $this->validationErrors = $this->validationErrors[$this->getModel()->alias]; + } + $this->getModel()->validationErrors = $this->validationErrors; + if (!$options['atomic']) { + return $return; + } + if ($return[$this->getModel()->alias] === false || !empty($this->validationErrors)) { + return false; + } + return true; + } + +/** + * Validates multiple individual records for a single model + * + * #### Options + * + * - 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(). + * @return boolean True on success, or false on failure. + * @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 validated successfully. + */ + public function validateMany($data, $options = array()) { + $options = array_merge(array('atomic' => true, 'deep' => false), $options); + $this->validationErrors = $validationErrors = $this->getModel()->validationErrors = $return = array(); + foreach ($data as $key => $record) { + if ($options['deep']) { + $validates = $this->validateAssociated($record, $options); + } else { + $validates = $this->getModel()->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[$key] = $validates; + } + $this->validationErrors = $this->getModel()->validationErrors = $validationErrors; + if (!$options['atomic']) { + return $return; + } + if (empty($this->validationErrors)) { + return true; + } + return false; + } + +/** + * 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 + * @see Model::validates() + */ + public function invalidFields($options = array()) { + if (!$this->propagateBeforeValidate($options)) { + return false; + } + $this->data = array(); + + $this->setOptions($options); + + if (!$this->setFields()) { + return $this->getModel()->validationErrors = $this->validationErrors; + } + + $this->getData(); + $this->getMethods(); + $this->setValidationDomain(); + + foreach ($this->_fields as $field) { + $field->validate(); + } + + $this->setFields(true); + + return $this->getModel()->validationErrors = $this->validationErrors; + } + +/** + * Marks a field as invalid, optionally setting the name of validation + * rule (in case of multiple validation for field) that was broken. + * + * @param string $field The name of the field to invalidate + * @param mixed $value Name of validation rule that was not failed, or validation message to + * be returned. If no validation key is provided, defaults to true. + * @return void + */ + public function invalidate($field, $value = true) { + if (!is_array($this->validationErrors)) { + $this->validationErrors = array(); + } + $this->validationErrors[$field][] = $this->getModel()->validationErrors[$field][] = $value; + } + +/** + * Gets the current data from the model and sets it to $this->data + * + * @param string $field [optional] + * @return array The data + */ + public function getData($field = null, $all = false) { + if (!empty($this->data)) { + if ($field !== null && isset($this->data[$field])) { + return $this->data[$field]; + } + return $this->data; + } + + $this->data = $this->_model->data; + if (FALSE === $all && isset($this->data[$this->_model->alias])) { + $this->data = $this->data[$this->_model->alias]; + } elseif (!is_array($this->data)) { + $this->data = array(); + } + + if ($field !== null && isset($this->data[$field])) { + return $this->data[$field]; + } + + return $this->data; + } + +/** + * Gets all possible custom methods from the Model, Behaviors and the Validator. + * If $type is null (default) gets all methods. If $type is one of 'model', 'behaviors' or 'validator', + * gets the corresponding methods. + * + * @param string $type [optional] The methods type to get. Defaults to null + * @return array The requested methods + */ + public function getMethods($type = null) { + if (!empty($this->_methods)) { + if ($type !== null && !empty($this->_methods[$type])) { + return $this->_methods[$type]; + } + return $this->_methods; + } + + $this->_methods['model'] = array_map('strtolower', get_class_methods($this->_model)); + $this->_methods['behaviors'] = array_keys($this->_model->Behaviors->methods()); + $this->_methods['validator'] = get_class_methods($this); + + if ($type !== null && !empty($this->_methods[$type])) { + return $this->_methods[$type]; + } + unset($type); + + return $this->_methods; + } + +/** + * Gets all fields if $name is null (default), or the field for fieldname $name if it's found. + * + * @param string $name [optional] The fieldname to fetch. Defaults to null. + * @return array|ModelField Either the fields array or the ModelField for fieldname $name + */ + public function getFields($name = null) { + if ($name !== null && !empty($this->_fields[$name])) { + return $this->_fields[$name]; + } + return $this->_fields; + } + +/** + * Sets the ModelField isntances from the Model::$validate property after processing the fieldList and whiteList. + * If Model::$validate is not set or empty, this method returns false. True otherwise. + * + * @param boolean $reset If true will reset the Validator $validate array to the Model's default + * @return boolean True if Model::$validate was processed, false otherwise + */ + public function setFields($reset = false) { + if (!isset($this->_model->validate) || empty($this->_model->validate)) { + $this->_validate = array(); + return false; + } + + $this->_validate = $this->_model->validate; + + if ($reset === true) { + return true; + } + + $this->_processWhitelist(); + + $this->_fields = array(); + foreach ($this->_validate as $fieldName => $ruleSet) { + $this->_fields[$fieldName] = new CakeField($this, $fieldName, $ruleSet); + } + unset($fieldName, $ruleSet); + return true; + } + +/** + * Sets an options array. If $mergeVars is true, the options will be merged with the existing ones. + * Otherwise they will get replaced. The default is merging the vars. + * + * @param array $options [optional] The options to be set + * @param boolean $mergeVars [optional] If true, the options will be merged, otherwise they get replaced + * @return ModelValidator + */ + public function setOptions($options = array(), $mergeVars = false) { + if ($mergeVars === false) { + $this->options = $options; + } else { + $this->options = array_merge($this->options, $options); + } + return $this; + } + +/** + * Sets an option $name with $value. This method is chainable + * + * @param string $name The options name to be set + * @param mixed $value [optional] The value to be set. Defaults to null. + * @return ModelValidator + */ + public function setOption($name, $value = null) { + $this->options[$name] = $value; + return $this; + } + +/** + * Gets an options value by $name. If $name is not set or no option has been found, returns null. + * + * @param string $name The options name to look up + * @return mixed Either null or the option value + */ + public function getOptions($name = NULL) { + if (NULL !== $name) { + if (!isset($this->options[$name])) { + return NULL; + } + return $this->options[$name]; + } + return $this->options; + } + +/** + * Sets the I18n domain for validation messages. This method is chainable. + * + * @param string $validationDomain [optional] The validation domain to be used. If none is given, uses Model::$validationDomain + * @return ModelValidator + */ + public function setValidationDomain($validationDomain = null) { + if ($validationDomain !== null) { + $this->validationDomain = $validationDomain; + } elseif ($this->_model->validationDomain !== null) { + $this->validationDomain = $this->_model->validationDomain; + } else { + $this->validationDomain = ModelValidator::DEFAULT_DOMAIN; + } + + return $this; + } + +/** + * Gets the parent Model + * + * @return Model + */ + public function getModel() { + return $this->_model; + } + +/** + * Processes the Model's whitelist and adjusts the validate array accordingly + * + * @return void + */ + protected function _processWhitelist() { + $whitelist = $this->getModel()->whitelist; + $fieldList = $this->getOptions('fieldList'); + + if (!empty($fieldList)) { + if (!empty($fieldList[$this->getModel()->alias]) && is_array($fieldList[$this->getModel()->alias])) { + $whitelist = $fieldList[$this->getModel()->alias]; + } else { + $whitelist = $fieldList; + } + } + unset($fieldList); + + if (!empty($whitelist)) { + $this->validationErrors = array(); + $validate = array(); + foreach ((array) $whitelist as $f) { + if (!empty($this->_validate[$f])) { + $validate[$f] = $this->_validate[$f]; + } + } + $this->_validate = $validate; + } + } + +/** + * 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 Validation of with models + * @return boolean Failure of validation on with models. + * @see Model::validates() + */ + protected function _validateWithModels($options) { + $valid = true; + $this->getData(null, true); + + foreach ($this->getModel()->hasAndBelongsToMany as $assoc => $association) { + if (empty($association['with']) || !isset($this->data[$assoc])) { + continue; + } + list($join) = $this->getModel()->joinModel($this->getModel()->hasAndBelongsToMany[$assoc]['with']); + $data = $this->data[$assoc]; + + $newData = array(); + foreach ((array)$data as $row) { + if (isset($row[$this->getModel()->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row; + } elseif (isset($row[$join]) && isset($row[$join][$this->getModel()->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + $newData[] = $row[$join]; + } + } + if (empty($newData)) { + continue; + } + foreach ($newData as $data) { + $data[$this->getModel()->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->getModel()->id; + $this->getModel()->{$join}->create($data); + $valid = ($valid && $this->getModel()->{$join}->getValidator()->validates($options)); + } + } + return $valid; + } + +/** + * Propagates the beforeValidate event + * + * @param array $options + * @return boolean + */ + public function propagateBeforeValidate($options = array()) { + $event = new CakeEvent('Model.beforeValidate', $this->getModel(), array($options)); + list($event->break, $event->breakOn) = array(true, false); + $this->getModel()->getEventManager()->dispatch($event); + if ($event->isStopped()) { + return false; + } + return true; + } + +} diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php new file mode 100644 index 000000000..887f75009 --- /dev/null +++ b/lib/Cake/Model/Validator/CakeField.php @@ -0,0 +1,188 @@ +_validator = $validator; + $this->data = &$this->getValidator()->data; + $this->field = $fieldName; + + if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { + $ruleSet = array($ruleSet); + } + + foreach ($ruleSet as $index => $validateProp) { + $this->_rules[$index] = new CakeRule($this, $validateProp, $index); + } + $this->ruleSet = $ruleSet; + unset($ruleSet, $validateProp); + } + +/** + * Validates a ModelField + * + * @return mixed + */ + public function validate() { + foreach ($this->getRules() as $rule) { + if ($rule->skip()) { + continue; + } + $rule->isRequired(); + + if (!$rule->checkRequired() && array_key_exists($this->field, $this->data)) { + if ($rule->checkEmpty()) { + break; + } + $rule->dispatchValidation(); + } + + if ($rule->checkRequired() || !$rule->isValid()) { + $this->getValidator()->invalidate($this->field, $rule->getMessage()); + + if ($rule->isLast()) { + return false; + } + } + } + + return true; + } + +/** + * Gets a rule for a certain index + * + * @param mixed index + * @return ValidationRule + */ + public function getRule($index) { + if (!empty($this->_rules[$index])) { + return $this->_rules[$index]; + } + } + +/** + * Gets all rules for this ModelField + * + * @return array + */ + public function getRules() { + return $this->_rules; + } + +/** + * Sets a ValidationRule $rule for key $key + * + * @param mixed $key The key under which the rule should be set + * @param ValidationRule $rule The ValidationRule to be set + * @return ModelField + */ + public function setRule($key, CakeRule $rule) { + $this->_rules[$key] = $rule; + return $this; + } + +/** + * Sets the rules for a given field + * + * @param array $rules The rules to be set + * @param bolean $mergeVars [optional] If true, merges vars instead of replace. Defaults to true. + * @return ModelField + */ + public function setRules($rules = array(), $mergeVars = true) { + if ($mergeVars === false) { + $this->_rules = $rules; + } else { + $this->_rules = array_merge($this->_rules, $rules); + } + return $this; + } + +/** + * Gets the validator this field is atached to + * + * @return ModelValidator The parent ModelValidator instance + */ + public function getValidator() { + return $this->_validator; + } + +/** + * Magic isset + * + * @return true if the field exists in data, false otherwise + */ + public function __isset($fieldName) { + return array_key_exists($fieldName, $this->getValidator()->getData()); + } + +} diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php new file mode 100644 index 000000000..54262184c --- /dev/null +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -0,0 +1,404 @@ +_field = $field; + $this->_index = $index; + unset($field, $index); + + $this->data = &$this->getField() + ->data; + + $this->_modelExists = $this->getField() + ->getValidator() + ->getModel() + ->exists(); + + $this->_addValidatorProps($validator); + unset($validator); + } + +/** + * Checks if the rule is valid + * + * @return boolean + */ + public function isValid() { + if (!$this->_valid || (is_string($this->_valid) && strlen($this->_valid) > 0)) { + return false; + } + + return true; + } + +/** + * Checks if the field is required by the 'required' value + * + * @return boolean + */ + public function isRequired() { + if ($this->required === true || $this->required === false) { + return $this->required; + } + + if (in_array($this->required, array('create', 'update'), true)) { + if ($this->required === 'create' && !$this->_modelExists || $this->required === 'update' && $this->_modelExists) { + $this->required = true; + } + } + + return $this->required; + } + +/** + * Checks if the field failed the required validation + * + * @return boolean + */ + public function checkRequired() { + if ($this->_requiredFail !== null) { + return $this->_requiredFail; + } + $this->_requiredFail = ( + (!isset($this->data[$this->getField()->field]) && $this->required === true) || + ( + isset($this->data[$this->getField()->field]) && (empty($this->data[$this->getField()->field]) && + !is_numeric($this->data[$this->getField()->field])) && $this->allowEmpty === false + ) + ); + return $this->_requiredFail; + } + +/** + * Checks if the allowEmpty key applies + * + * @return boolean + */ + public function checkEmpty() { + if ($this->_emptyFail !== null) { + return $this->_emptyFail; + } + $this->_emptyFail = false; + + if (empty($this->data[$this->getField()->field]) && $this->data[$this->getField()->field] != '0' && $this->allowEmpty === true) { + $this->_emptyFail = true; + } + return $this->_emptyFail; + } + +/** + * Checks if the Validation rule can be skipped + * + * @return boolean True if the ValidaitonRule can be skipped + */ + public function skip() { + if (!empty($this->on)) { + if ($this->on == 'create' && $this->_modelExists || $this->on == 'update' && !$this->_modelExists) { + return true; + } + } + return false; + } + +/** + * Checks if the 'last' key is true + * + * @return boolean + */ + public function isLast() { + return (bool) $this->last; + } + +/** + * Gets the validation error message + * + * @return string + */ + public function getMessage() { + return $this->_processValidationResponse(); + } + +/** + * Gets the parent field + * + * @return ModelField + */ + public function getField() { + return $this->_field; + } + +/** + * Gets an array with the rule properties + * + * @return array + */ + public function getPropertiesArray() { + return array( + 'rule' => $this->rule, + 'required' => $this->required, + 'allowEmpty' => $this->allowEmpty, + 'on' => $this->on, + 'last' => $this->last, + 'message' => $this->message + ); + } + +/** + * Dispatches the validation rule to the given validator method + * + * @return boolean True if the rule could be dispatched, false otherwise + */ + public function dispatchValidation() { + $this->_parseRule(); + + $validator = $this->getPropertiesArray(); + $methods = $this->getField()->getValidator()->getMethods(); + $Model = $this->getField()->getValidator()->getModel(); + + if (in_array(strtolower($this->_rule), $methods['model'])) { + $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); + $this->_ruleParams[0] = array($this->getField()->field => $this->_ruleParams[0]); + $this->_valid = $Model->dispatchMethod($this->_rule, $this->_ruleParams); + } elseif (in_array($this->_rule, $methods['behaviors']) || in_array(strtolower($this->_rule), $methods['behaviors'])) { + $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); + $this->_ruleParams[0] = array($this->getField()->field => $this->_ruleParams[0]); + $this->_valid = $Model->Behaviors->dispatchMethod($Model, $this->_rule, $this->_ruleParams); + } elseif (method_exists('Validation', $this->_rule)) { + $this->_valid = call_user_func_array(array('Validation', $this->_rule), $this->_ruleParams); + } elseif (!is_array($validator['rule'])) { + $this->_valid = preg_match($this->_rule, $this->data[$this->getField()->field]); + } elseif (Configure::read('debug') > 0) { + trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field->field), E_USER_WARNING); + return false; + } + unset($validator, $methods, $Model); + + return true; + } + +/** + * Fetches the correct error message for a failed validation + * + * @return string + */ + protected function _processValidationResponse() { + $validationDomain = $this->_field->getValidator()->validationDomain; + + if (is_string($this->_valid)) { + $this->_errorMessage = $this->_valid; + } elseif ($this->message !== null) { + $args = null; + if (is_array($this->message)) { + $this->_errorMessage = $this->message[0]; + $args = array_slice($this->message, 1); + } else { + $this->_errorMessage = $this->message; + } + if (is_array($this->rule) && $args === null) { + $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); + } + $this->_errorMessage = __d($validationDomain, $this->_errorMessage, $args); + } elseif (is_string($this->_index)) { + if (is_array($this->rule)) { + $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); + $this->_errorMessage = __d($validationDomain, $this->_index, $args); + } else { + $this->_errorMessage = __d($validationDomain, $this->_index); + } + } elseif (!$this->checkRequired() && is_numeric($this->_index) && count($this->getField()->ruleSet) > 1) { + $this->_errorMessage = $this->_index + 1; + } else { + $this->_errorMessage = __d('cake_dev', 'This field cannot be left blank'); + } + unset($validationDomain); + + return $this->_errorMessage; + } + +/** + * Sets the rule properties from the rule entry in validate + * + * @param array $validator [optional] + * @return void + */ + protected function _addValidatorProps($validator = array()) { + if (!is_array($validator)) { + $validator = array('rule' => $validator); + } + foreach ($validator as $key => $value) { + if (isset($value) || !empty($value)) { + if (in_array($key, array('rule', 'required', 'allowEmpty', 'on', 'message', 'last'))) { + $this->$key = $validator[$key]; + } else { + $this->_passedOptions[$key] = $value; + } + } + } + unset($validator); + } + +/** + * Parses the rule and sets the rule and ruleParams + * + * @return void + */ + protected function _parseRule() { + if (is_array($this->rule)) { + $this->_rule = $this->rule[0]; + unset($this->rule[0]); + $this->_ruleParams = array_merge(array($this->data[$this->getField()->field]), array_values($this->rule)); + } else { + $this->_rule = $this->rule; + $this->_ruleParams = array($this->data[$this->getField()->field]); + } + } + +} diff --git a/lib/Cake/Test/Case/Controller/ControllerTest.php b/lib/Cake/Test/Case/Controller/ControllerTest.php index b77a35bf3..83b791ccb 100644 --- a/lib/Cake/Test/Case/Controller/ControllerTest.php +++ b/lib/Cake/Test/Case/Controller/ControllerTest.php @@ -387,7 +387,7 @@ class AnotherTestController extends ControllerTestAppController { /** * merge parent - * + * * @var string */ protected $_mergeParent = 'ControllerTestAppController'; diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 8c0be4162..b41a95c28 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -48,7 +48,8 @@ class ModelValidationTest extends BaseModelTest { 'on' => null, 'last' => true, 'allowEmpty' => false, - 'required' => true + 'required' => true, + 'message' => null ), 'or' => true, 'ignoreOnSame' => 'id' @@ -84,7 +85,8 @@ class ModelValidationTest extends BaseModelTest { 'on' => null, 'last' => true, 'allowEmpty' => false, - 'required' => true + 'required' => true, + 'message' => null ), 'six' => 6 ); @@ -110,7 +112,8 @@ class ModelValidationTest extends BaseModelTest { 'on' => null, 'last' => true, 'allowEmpty' => false, - 'required' => true + 'required' => true, + 'message' => null ) ); $this->assertEquals($expected, $TestModel->validatorParams); @@ -346,6 +349,7 @@ class ModelValidationTest extends BaseModelTest { $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 05c34dab5..76963ebaf 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -107,7 +107,7 @@ class ModelWriteTest extends BaseModelTest { $testResult = $Article->find('first', array('conditions' => array('Article.title' => 'Test Title'))); - $this->assertEquals($testResult['Article']['title'], $data['Article']['title']); + $this->assertEquals($data['Article']['title'], $testResult['Article']['title']); $this->assertEquals('2008-01-01 00:00:00', $testResult['Article']['created']); } @@ -151,8 +151,8 @@ class ModelWriteTest extends BaseModelTest { $TestModel->save(array('title' => 'Test record')); $result = $TestModel->findByTitle('Test record'); $this->assertEquals( - array_keys($result['Uuid']), - array('id', 'title', 'count', 'created', 'updated') + array('id', 'title', 'count', 'created', 'updated'), + array_keys($result['Uuid']) ); $this->assertEquals(36, strlen($result['Uuid']['id'])); } @@ -173,8 +173,8 @@ class ModelWriteTest extends BaseModelTest { $TestModel->save(array('title' => 'Test record', 'id' => null)); $result = $TestModel->findByTitle('Test record'); $this->assertEquals( - array_keys($result['Uuid']), - array('id', 'title', 'count', 'created', 'updated') + array('id', 'title', 'count', 'created', 'updated'), + array_keys($result['Uuid']) ); $this->assertEquals(36, strlen($result['Uuid']['id'])); } @@ -2345,7 +2345,7 @@ class ModelWriteTest extends BaseModelTest { 'User' => array( 'user' => 'updated user' ))); - $this->assertEquals($TestModel->id, $id); + $this->assertEquals($id, $TestModel->id); $result = $TestModel->findById($id); $this->assertEquals('updated user', $result['User']['user']); @@ -2940,7 +2940,7 @@ class ModelWriteTest extends BaseModelTest { $model->Attachment->validate = array('attachment' => 'notEmpty'); $model->Attachment->bindModel(array('belongsTo' => array('Comment'))); - $this->assertEquals($model->saveAll( + $result = $model->saveAll( array( 'Comment' => array( 'comment' => '', @@ -2950,7 +2950,8 @@ class ModelWriteTest extends BaseModelTest { 'Attachment' => array('attachment' => '') ), array('validate' => 'first') - ), false); + ); + $this->assertEquals(false, $result); $expected = array( 'Comment' => array('comment' => array('This field cannot be left blank')), 'Attachment' => array('attachment' => array('This field cannot be left blank')) @@ -4329,7 +4330,7 @@ class ModelWriteTest extends BaseModelTest { $this->assertTrue(Set::matches('/Post[2][title=Just update the title]', $result)); } - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); $TestModel->validate = array('title' => 'notEmpty', 'author_id' => 'numeric'); $data = array( @@ -4400,7 +4401,7 @@ class ModelWriteTest extends BaseModelTest { $result[3]['Post']['updated'], $result[3]['Post']['created'] ); $this->assertEquals($expected, $result); - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); $data = array( array( @@ -4422,7 +4423,7 @@ class ModelWriteTest extends BaseModelTest { $result[3]['Post']['updated'], $result[3]['Post']['created'] ); $this->assertEquals($expected, $result); - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); } /** @@ -4550,8 +4551,8 @@ class ModelWriteTest extends BaseModelTest { $result = $model->find('all'); $this->assertEquals( - $result[0]['Article']['title'], - 'Post with Author saveAlled from comment' + 'Post with Author saveAlled from comment', + $result[0]['Article']['title'] ); $this->assertEquals('Only new comment', $result[0]['Comment'][0]['comment']); } @@ -5037,7 +5038,7 @@ class ModelWriteTest extends BaseModelTest { $model->Attachment->validate = array('attachment' => 'notEmpty'); $model->Attachment->bindModel(array('belongsTo' => array('Comment'))); - $this->assertEquals($model->saveAssociated( + $result = $model->saveAssociated( array( 'Comment' => array( 'comment' => '', @@ -5046,7 +5047,8 @@ class ModelWriteTest extends BaseModelTest { ), 'Attachment' => array('attachment' => '') ) - ), false); + ); + $this->assertFalse($result); $expected = array( 'Comment' => array('comment' => array('This field cannot be left blank')), 'Attachment' => array('attachment' => array('This field cannot be left blank')) @@ -5688,7 +5690,7 @@ class ModelWriteTest extends BaseModelTest { $this->assertTrue(Set::matches('/Post[2][title=Just update the title]', $result)); } - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); $TestModel->validate = array('title' => 'notEmpty', 'author_id' => 'numeric'); $data = array( @@ -5746,7 +5748,7 @@ class ModelWriteTest extends BaseModelTest { 'published' => 'N', ))); $this->assertEquals($expected, $result); - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); $data = array( array( @@ -5768,7 +5770,7 @@ class ModelWriteTest extends BaseModelTest { 'order' => 'Post.id ASC' )); $this->assertEquals($expected, $result); - $this->assertEquals($TestModel->validationErrors, $errors); + $this->assertEquals($errors, $TestModel->validationErrors); } /** @@ -5876,8 +5878,8 @@ class ModelWriteTest extends BaseModelTest { $result = $model->find('all'); $this->assertEquals( - $result[0]['Article']['title'], - 'Post with Author saveAlled from comment' + 'Post with Author saveAlled from comment', + $result[0]['Article']['title'] ); $this->assertEquals('Only new comment', $result[0]['Comment'][0]['comment']); } From ec9966ec6bc7f03dda59bdab0bfa4eef3189e1a9 Mon Sep 17 00:00:00 2001 From: Ceeram Date: Mon, 23 Apr 2012 13:02:26 +0200 Subject: [PATCH 02/42] Adding tests for Validator --- lib/Cake/Model/Model.php | 3 +- lib/Cake/Model/ModelValidator.php | 10 +- lib/Cake/Model/Validator/CakeField.php | 36 +- lib/Cake/Model/Validator/CakeRule.php | 94 +- lib/Cake/Test/Case/Model/ModelTest.php | 2 + .../Test/Case/Model/ModelValidationTest.php | 918 +++++++++++++++++- lib/Cake/Test/Case/Model/ModelWriteTest.php | 712 -------------- .../Case/Model/Validator/CakeFieldTest.php | 181 ++++ .../Case/Model/Validator/CakeRuleTest.php | 52 + 9 files changed, 1224 insertions(+), 784 deletions(-) create mode 100644 lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php create mode 100644 lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index af7131c72..3dd85bc81 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -979,7 +979,8 @@ class Model extends Object implements CakeEventListener { if (strpos($assoc, '.') !== false) { list($plugin, $assoc) = pluginSplit($assoc, true); - $this->{$type}[$assoc] = array('className' => $plugin . $assoc); } else { + $this->{$type}[$assoc] = array('className' => $plugin . $assoc); + } else { $this->{$type}[$assoc] = $value; } } diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 8bafa2cbb..6b92f969e 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -15,7 +15,7 @@ * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package Cake.Model - * @since CakePHP(tm) v 0.10.0.0 + * @since CakePHP(tm) v 2.2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::uses('CakeField', 'Model/Validator'); @@ -72,7 +72,7 @@ class ModelValidator { public $options = array(); /** - * Holds the ModelFields + * Holds the CakeField objects array * * @var array */ @@ -345,17 +345,19 @@ class ModelValidator { * Gets all fields if $name is null (default), or the field for fieldname $name if it's found. * * @param string $name [optional] The fieldname to fetch. Defaults to null. - * @return array|ModelField Either the fields array or the ModelField for fieldname $name + * @return mixed Either array of CakeField objects , single object for $name or false when $name not present in fields */ public function getFields($name = null) { if ($name !== null && !empty($this->_fields[$name])) { return $this->_fields[$name]; + } elseif ($name !==null) { + return false; } return $this->_fields; } /** - * Sets the ModelField isntances from the Model::$validate property after processing the fieldList and whiteList. + * Sets the CakeField isntances from the Model::$validate property after processing the fieldList and whiteList. * If Model::$validate is not set or empty, this method returns false. True otherwise. * * @param boolean $reset If true will reset the Validator $validate array to the Model's default diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php index 887f75009..9bd6fea60 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeField.php @@ -14,62 +14,62 @@ * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package Cake.Model - * @since CakePHP(tm) v 3.0.0 + * @package Cake.Model.Validator + * @since CakePHP(tm) v 2.2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::uses('ModelValidator', 'Model'); App::uses('CakeRule', 'Model/Validator'); /** - * ModelField object. + * CakeField object. * - * @package Cake.Model + * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html */ class CakeField { /** * Holds the parent Validator instance - * + * * @var ModelValidator */ protected $_validator = null; /** * Holds the ValidationRule objects - * + * * @var array */ protected $_rules = array(); /** * If the validation is stopped - * + * * @var boolean */ public $isStopped = false; /** * Holds the fieldname - * + * * @var string */ public $field = null; /** * Holds the original ruleSet - * + * * @var array */ public $ruleSet = array(); /** * Constructor - * + * * @param ModelValidator $validator The parent ModelValidator * @param string $fieldName The fieldname - * @param + * @param */ public function __construct(ModelValidator $validator, $fieldName, $ruleSet) { $this->_validator = $validator; @@ -89,7 +89,7 @@ class CakeField { /** * Validates a ModelField - * + * * @return mixed */ public function validate() { @@ -120,7 +120,7 @@ class CakeField { /** * Gets a rule for a certain index - * + * * @param mixed index * @return ValidationRule */ @@ -132,7 +132,7 @@ class CakeField { /** * Gets all rules for this ModelField - * + * * @return array */ public function getRules() { @@ -141,7 +141,7 @@ class CakeField { /** * Sets a ValidationRule $rule for key $key - * + * * @param mixed $key The key under which the rule should be set * @param ValidationRule $rule The ValidationRule to be set * @return ModelField @@ -153,7 +153,7 @@ class CakeField { /** * Sets the rules for a given field - * + * * @param array $rules The rules to be set * @param bolean $mergeVars [optional] If true, merges vars instead of replace. Defaults to true. * @return ModelField @@ -169,7 +169,7 @@ class CakeField { /** * Gets the validator this field is atached to - * + * * @return ModelValidator The parent ModelValidator instance */ public function getValidator() { @@ -178,7 +178,7 @@ class CakeField { /** * Magic isset - * + * * @return true if the field exists in data, false otherwise */ public function __isset($fieldName) { diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 54262184c..c3cc7f738 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -14,8 +14,8 @@ * * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project - * @package Cake.Model - * @since CakePHP(tm) v 3.0.0 + * @package Cake.Model.Validator + * @since CakePHP(tm) v 2.2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::uses('ModelValidator', 'Model'); @@ -23,129 +23,129 @@ App::uses('CakeField', 'Model/Validator'); App::uses('Validation', 'Utility'); /** - * ValidationRule object. + * CakeRule object. * - * @package Cake.Model + * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html */ class CakeRule { /** * Holds a reference to the parent field - * - * @var ModelField + * + * @var CakeField */ protected $_field = null; /** * Has the required check failed? - * + * * @var boolean */ protected $_requiredFail = null; /** * The 'valid' value - * + * * @var mixed */ protected $_valid = true; /** - * Holds the index under which the Vaildator was attached - * + * Holds the index under which the Validator was attached + * * @var mixed */ protected $_index = null; /** * Create or Update transaction? - * + * * @var boolean */ protected $_modelExists = null; /** * The parsed rule - * + * * @var mixed */ protected $_rule = null; /** * The parsed rule parameters - * + * * @var array */ protected $_ruleParams = array(); /** * The errorMessage - * + * * @var string */ protected $_errorMessage = null; /** * Holds passed in options - * + * * @var array */ protected $_passedOptions = array(); /** * Flag indicating wether the allowEmpty check has failed - * - * @var boolean + * + * @var boolean */ protected $_emptyFail = null; /** * The 'rule' key - * + * * @var mixed */ public $rule = 'blank'; /** * The 'required' key - * + * * @var mixed */ public $required = null; /** * The 'allowEmpty' key - * + * * @var boolean */ public $allowEmpty = false; /** * The 'on' key - * + * * @var string */ public $on = null; /** * The 'last' key - * + * * @var boolean */ public $last = true; /** * The 'message' key - * + * * @var string */ public $message = null; /** * Constructor - * - * @param ModelField $field + * + * @param CakeField $field * @param array $validator [optional] The validator properties * @param mixed $index [optional] */ @@ -168,11 +168,11 @@ class CakeRule { /** * Checks if the rule is valid - * + * * @return boolean */ public function isValid() { - if (!$this->_valid || (is_string($this->_valid) && strlen($this->_valid) > 0)) { + if (!$this->_valid || (is_string($this->_valid) && !empty($this->_valid))) { return false; } @@ -181,11 +181,11 @@ class CakeRule { /** * Checks if the field is required by the 'required' value - * + * * @return boolean */ public function isRequired() { - if ($this->required === true || $this->required === false) { + if (is_bool($this->required)) { return $this->required; } @@ -200,7 +200,7 @@ class CakeRule { /** * Checks if the field failed the required validation - * + * * @return boolean */ public function checkRequired() { @@ -219,7 +219,7 @@ class CakeRule { /** * Checks if the allowEmpty key applies - * + * * @return boolean */ public function checkEmpty() { @@ -236,8 +236,8 @@ class CakeRule { /** * Checks if the Validation rule can be skipped - * - * @return boolean True if the ValidaitonRule can be skipped + * + * @return boolean True if the ValidationRule can be skipped */ public function skip() { if (!empty($this->on)) { @@ -250,7 +250,7 @@ class CakeRule { /** * Checks if the 'last' key is true - * + * * @return boolean */ public function isLast() { @@ -259,7 +259,7 @@ class CakeRule { /** * Gets the validation error message - * + * * @return string */ public function getMessage() { @@ -268,8 +268,8 @@ class CakeRule { /** * Gets the parent field - * - * @return ModelField + * + * @return CakeField */ public function getField() { return $this->_field; @@ -277,7 +277,7 @@ class CakeRule { /** * Gets an array with the rule properties - * + * * @return array */ public function getPropertiesArray() { @@ -293,7 +293,7 @@ class CakeRule { /** * Dispatches the validation rule to the given validator method - * + * * @return boolean True if the rule could be dispatched, false otherwise */ public function dispatchValidation() { @@ -326,7 +326,7 @@ class CakeRule { /** * Fetches the correct error message for a failed validation - * + * * @return string */ protected function _processValidationResponse() { @@ -345,10 +345,20 @@ class CakeRule { if (is_array($this->rule) && $args === null) { $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); } + if (!empty($args)) { + foreach ($args as $k => $arg) { + $args[$k] = __d($validationDomain, $arg); + } + } $this->_errorMessage = __d($validationDomain, $this->_errorMessage, $args); } elseif (is_string($this->_index)) { if (is_array($this->rule)) { $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); + if (!empty($args)) { + foreach ($args as $k => $arg) { + $args[$k] = __d($validationDomain, $arg); + } + } $this->_errorMessage = __d($validationDomain, $this->_index, $args); } else { $this->_errorMessage = __d($validationDomain, $this->_index); @@ -365,7 +375,7 @@ class CakeRule { /** * Sets the rule properties from the rule entry in validate - * + * * @param array $validator [optional] * @return void */ @@ -387,7 +397,7 @@ class CakeRule { /** * Parses the rule and sets the rule and ruleParams - * + * * @return void */ protected function _parseRule() { diff --git a/lib/Cake/Test/Case/Model/ModelTest.php b/lib/Cake/Test/Case/Model/ModelTest.php index a451c0364..e5e1567f2 100644 --- a/lib/Cake/Test/Case/Model/ModelTest.php +++ b/lib/Cake/Test/Case/Model/ModelTest.php @@ -34,6 +34,8 @@ class ModelTest extends PHPUnit_Framework_TestSuite { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('All Model related class tests'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeFieldTest.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeRuleTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelReadTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelWriteTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelDeleteTest.php'); diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index b41a95c28..b828f08b6 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -32,6 +32,7 @@ class ModelValidationTest extends BaseModelTest { */ public function testValidationParams() { $TestModel = new ValidationTest1(); + $Validator = new ModelValidator($TestModel); $TestModel->validate['title'] = array( 'rule' => 'customValidatorWithParams', 'required' => true @@ -55,6 +56,8 @@ class ModelValidationTest extends BaseModelTest { 'ignoreOnSame' => 'id' ); $this->assertEquals($expected, $TestModel->validatorParams); + $Validator->invalidFields(); + $this->assertEquals($expected, $TestModel->validatorParams); $TestModel->validate['title'] = array( 'rule' => 'customValidatorWithMessage', @@ -65,6 +68,7 @@ class ModelValidationTest extends BaseModelTest { ); $this->assertEquals($expected, $TestModel->invalidFields()); + $this->assertEquals($expected, $Validator->invalidFields()); $TestModel->validate['title'] = array( 'rule' => array('customValidatorWithSixParams', 'one', 'two', null, 'four'), @@ -91,6 +95,8 @@ class ModelValidationTest extends BaseModelTest { 'six' => 6 ); $this->assertEquals($expected, $TestModel->validatorParams); + $Validator->invalidFields(); + $this->assertEquals($expected, $TestModel->validatorParams); $TestModel->validate['title'] = array( 'rule' => array('customValidatorWithSixParams', 'one', array('two'), null, 'four', array('five' => 5)), @@ -117,6 +123,8 @@ class ModelValidationTest extends BaseModelTest { ) ); $this->assertEquals($expected, $TestModel->validatorParams); + $Validator->invalidFields(); + $this->assertEquals($expected, $TestModel->validatorParams); } /** @@ -126,6 +134,7 @@ class ModelValidationTest extends BaseModelTest { */ public function testInvalidFieldsWithFieldListParams() { $TestModel = new ValidationTest1(); + $Validator = new ModelValidator($TestModel); $TestModel->validate = $validate = array( 'title' => array( 'rule' => 'alphaNumeric', @@ -141,14 +150,18 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->validationErrors = array(); + $Validator->invalidFields(array('fieldList' => array('title'))); + $this->assertEquals($expected, $Validator->validationErrors); + $TestModel->validationErrors = $Validator->validatiorErrors = array(); $TestModel->invalidFields(array('fieldList' => array('name'))); $expected = array( 'name' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->validationErrors = array(); + $Validator->invalidFields(array('fieldList' => array('name'))); + $this->assertEquals($expected, $Validator->validationErrors); + $TestModel->validationErrors = $Validator->validatiorErrors = array(); $TestModel->invalidFields(array('fieldList' => array('name', 'title'))); $expected = array( @@ -156,12 +169,16 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->validationErrors = array(); + $Validator->invalidFields(array('fieldList' => array('title', 'name'))); + $this->assertEquals($expected, $Validator->validationErrors); + $TestModel->validationErrors = $Validator->validatiorErrors = array(); $TestModel->whitelist = array('name'); $TestModel->invalidFields(); $expected = array('name' => array('This field cannot be left blank')); $this->assertEquals($expected, $TestModel->validationErrors); + $Validator->invalidFields(); + $this->assertEquals($expected, $Validator->validationErrors); $this->assertEquals($TestModel->validate, $validate); } @@ -173,6 +190,7 @@ class ModelValidationTest extends BaseModelTest { */ public function testInvalidFieldsWhitelist() { $TestModel = new ValidationTest1(); + $Validator = $TestModel->getValidator(); $TestModel->validate = array( 'title' => array( 'rule' => 'alphaNumeric', @@ -188,6 +206,7 @@ class ModelValidationTest extends BaseModelTest { $expected = array('name' => array('This field cannot be left blank')); $this->assertEquals($expected, $TestModel->validationErrors); + $this->assertEquals($expected, $Validator->validationErrors); } /** @@ -197,6 +216,7 @@ class ModelValidationTest extends BaseModelTest { */ public function testValidates() { $TestModel = new TestValidate(); + $Validator = new ModelValidator($TestModel); $TestModel->validate = array( 'user_id' => 'numeric', @@ -213,6 +233,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -221,6 +243,8 @@ class ModelValidationTest extends BaseModelTest { )); $result = $TestModel->create($data) && $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -231,6 +255,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -241,6 +267,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate['modified'] = array('allowEmpty' => true, 'rule' => 'date'); @@ -254,6 +282,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -265,6 +295,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -276,6 +308,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -287,6 +321,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -298,6 +334,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $TestModel->validate['modified'] = array('allowEmpty' => false, 'rule' => 'date'); @@ -306,12 +344,16 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array('modified' => false)); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array('modified' => '')); $result = $TestModel->create($data); @@ -339,6 +381,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -349,7 +393,10 @@ class ModelValidationTest extends BaseModelTest { $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); - + $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); + $result = $Validator->validates(); $this->assertTrue($result); $data = array('TestValidate' => array( @@ -362,6 +409,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $TestModel->validate = array( 'number' => array( @@ -382,6 +431,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -391,6 +442,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -400,6 +453,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -409,6 +464,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate = array( 'number' => array( @@ -429,6 +486,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -438,6 +497,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $TestModel->validate = array( 'title' => array( @@ -450,18 +511,24 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array('title' => 'new title')); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array('title' => 'title-new')); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate = array('title' => array( 'allowEmpty' => true, @@ -472,6 +539,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -484,6 +553,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -494,6 +565,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); + $result = $Validator->validates(); + $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'My Article With a Different Title' @@ -502,6 +575,8 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); + $result = $Validator->validates(); + $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -520,6 +595,10 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('tooShort') ); $this->assertEquals($expected, $result); + $result = $Validator->validates(); + $this->assertFalse($result); + $result = $Validator->validationErrors; + $this->assertEquals($expected, $result); $TestModel->validate = array( 'title' => array( @@ -541,6 +620,10 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('tooShort', 'onlyLetters') ); $this->assertEquals($expected, $result); + $result = $Validator->validates(); + $this->assertFalse($result); + $result = $TestModel->validationErrors; + $this->assertEquals($expected, $result); } /** @@ -565,6 +648,7 @@ class ModelValidationTest extends BaseModelTest { $Something = new Something(); $JoinThing = $Something->JoinThing; + $Validator = $Something->JoinThing->getValidator(); $JoinThing->validate = array('doomed' => array('rule' => 'notEmpty')); @@ -574,6 +658,7 @@ class ModelValidationTest extends BaseModelTest { $result = $Something->save($data); $this->assertFalse($result, 'Save occurred even when with models failed. %s'); $this->assertEquals($expectedError, $JoinThing->validationErrors); + $this->assertEquals($expectedError, $Validator->validationErrors); $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); $this->assertSame($count, 0); @@ -625,12 +710,18 @@ class ModelValidationTest extends BaseModelTest { $Something->create(); $result = $Something->saveAll($data, array('validate' => 'only')); $this->assertFalse($result); + $result = $Something->validateAssociated($data); + $this->assertFalse($result); $this->assertEquals($expectedError, $JoinThing->validationErrors); + $result = $Something->getValidator()->validateAssociated($data); + $this->assertFalse($result); + $this->assertEquals($expectedError, $JoinThing->getValidator()->validationErrors); $Something->create(); $result = $Something->saveAll($data, array('validate' => 'first')); $this->assertFalse($result); $this->assertEquals($expectedError, $JoinThing->validationErrors); + $this->assertEquals($expectedError, $JoinThing->getValidator()->validationErrors); $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); $this->assertSame($count, 0); @@ -667,6 +758,10 @@ class ModelValidationTest extends BaseModelTest { $Author->create(); $result = $Author->saveAll($data, array('validate' => 'only')); $this->assertTrue($result); + $result = $Author->validateAssociated($data); + $this->assertTrue($result); + $result = $Author->getValidator()->validateAssociated($data); + $this->assertTrue($result); $Author->create(); $result = $Author->saveAll($data, array('validate' => 'first')); @@ -721,6 +816,8 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->invalidFields(array('fieldList' => array('title'))); $this->assertEquals(array(), $TestModel->validationErrors); + $TestModel->getValidator()->invalidFields(array('fieldList' => array('title'))); + $this->assertEquals(array(), $TestModel->getValidator()->validationErrors); } /** @@ -746,23 +843,31 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->create(); - $TestModel->invalidFields(); $expected = array( 'title' => array( 'Minimum length allowed is 6 chars', ) ); + $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); + $TestModel->getValidator()->validationErrors = array(); + $TestModel->getValidator()->invalidFields(); + $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); + $TestModel->create(array('title' => 'foo')); - $TestModel->invalidFields(); $expected = array( 'title' => array( 'Minimum length allowed is 6 chars', 'You may enter up to 14 chars (minimum is 6 chars)' ) ); + $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); + + $TestModel->getValidator()->validationErrors = array(); + $TestModel->getValidator()->invalidFields(); + $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); } /** @@ -790,14 +895,18 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->create(); - $TestModel->invalidFields(); $expected = array( 'title' => array( 'Translated validation failed: Translated arg1', ) ); + $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); + $TestModel->getValidator()->validationErrors = array(); + $TestModel->getValidator()->invalidFields(); + $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); + $TestModel->validationDomain = 'default'; Configure::write('Config.language', $lang); App::build(); @@ -831,11 +940,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); + $this->assertFalse($Article->getValidator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); + $this->assertTrue($Article->getValidator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -849,11 +960,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); + $this->assertTrue($Article->getValidator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); + $this->assertFalse($Article->getValidator()->validates()); } /** @@ -884,11 +997,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); + $this->assertFalse($Article->getValidator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); + $this->assertTrue($Article->getValidator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -902,11 +1017,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); + $this->assertTrue($Article->getValidator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); + $this->assertFalse($Article->getValidator()->validates()); } /** @@ -1033,4 +1150,791 @@ class ModelValidationTest extends BaseModelTest { $this->assertFalse($Article->validates()); } +/** + * 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'; + + $data = 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) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => true)); + $this->assertTrue($result); + $result = $TestModel->validateAssociated($data, array('deep' => true)); + $this->assertTrue($result); + $result = $TestModel->getValidator()->validateAssociated($data, array('deep' => true)); + $this->assertTrue($result); + + $data = 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) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => true)); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($data, array('deep' => true)); + $this->assertFalse($result); + $result = $TestModel->getValidator()->validateAssociated($data, array('deep' => true)); + $this->assertFalse($result); + + $data = 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) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + true, + true + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->getValidator()->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + + $data = 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) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + false, + true + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + + $data = 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')) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => true)); + $this->assertTrue($result); + $result = $TestModel->validateAssociated($data, array('deep' => true)); + $this->assertTrue($result); + + $data = 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' => '')) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => true)); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($data, array('deep' => true)); + $this->assertFalse($result); + + $data = 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')) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + true, + true + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + + $data = 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' => '')) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + true, + false + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $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->validateAssociated($data, array('deep' => true)); + $this->assertTrue($result); + + $expected = array( + 'Attachment' => true, + 'Comment' => true + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertSame($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => 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->validateAssociated($data, array('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); + + $expected = array( + 'Attachment' => true, + 'Comment' => false + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $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->validateAssociated($data, array('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); + + $expected = array( + 'Attachment' => true, + 'Comment' => false + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $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->validateAssociated($data, array('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); + + $expected = array( + 'Attachment' => true, + 'Comment' => false + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $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->validateAssociated($data, array('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); + + $expected = array( + 'Attachment' => false, + 'Comment' => false + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => true)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $this->assertEquals($expected, $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'; + + $data = 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) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => false)); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($data, array('deep' => false)); + $this->assertFalse($result); + + $expected = array('body' => array('This field cannot be left blank')); + $result = $TestModel->validationErrors; + $this->assertSame($expected, $result); + + $data = 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) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => false)); + $this->assertTrue($result); + $result = $TestModel->validateAssociated($data, array('deep' => false)); + $this->assertTrue($result); + + $data = 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) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + true, + true + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => false)); + $this->assertSame($expected, $result); + + $data = 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' => '')) + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'deep' => false)); + $this->assertTrue($result); + $result = $TestModel->validateAssociated($data, array('deep' => false)); + $this->assertTrue($result); + + $data = 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' => '')) + ) + ); + $expected = array( + 'Article' => true, + 'Comment' => array( + true, + true + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false)); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => false)); + $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->validateAssociated($data, array('deep' => false)); + $this->assertTrue($result); + + $result = $TestModel->Comment->Attachment->validationErrors; + $expected = array(); + $this->assertSame($expected, $result); + + $expected = array( + 'Attachment' => true, + 'Comment' => true + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => false)); + $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->validateAssociated($data, array('deep' => false)); + $this->assertTrue($result); + + $result = $TestModel->Comment->Attachment->validationErrors; + $expected = array(); + $this->assertSame($expected, $result); + + $expected = array( + 'Attachment' => true, + 'Comment' => true + ); + $result = $TestModel->Comment->Attachment->saveAll($data, array('validate' => 'only', 'atomic' => false, 'deep' => false)); + $this->assertEquals($expected, $result); + $result = $TestModel->Comment->Attachment->validateAssociated($data, array('atomic' => false, 'deep' => false)); + $this->assertEquals($expected, $result); + } + +/** + * testValidateAssociated method + * + * @return void + */ + public function testValidateAssociated() { + $this->loadFixtures('Comment', 'Attachment'); + $TestModel = new Comment(); + $TestModel->Attachment->validate = array('attachment' => 'notEmpty'); + + $data = array( + 'Comment' => array( + 'comment' => 'This is the comment' + ), + 'Attachment' => array( + 'attachment' => '' + ) + ); + $result = $TestModel->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($data); + $this->assertFalse($result); + + $TestModel->validate = array('comment' => 'notEmpty'); + $record = array( + 'Comment' => array( + 'user_id' => 1, + 'article_id' => 1, + 'comment' => '', + ), + 'Attachment' => array( + 'attachment' => '' + ) + ); + $result = $TestModel->saveAll($record, array('validate' => 'only')); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($record); + $this->assertFalse($result); + + $fieldList = array( + 'Comment' => array('id', 'article_id', 'user_id'), + 'Attachment' => array('comment_id') + ); + $result = $TestModel->saveAll($record, array( + 'fieldList' => $fieldList, 'validate' => 'only' + )); + $this->assertTrue($result); + $this->assertEmpty($TestModel->validationErrors); + $result = $TestModel->validateAssociated($record, array('fieldList' => $fieldList)); + $this->assertTrue($result); + $this->assertEmpty($TestModel->validationErrors); + + $TestModel = new Article(); + $TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array(); + $TestModel->Comment->validate = array('comment' => 'notEmpty'); + $data = array( + 'Article' => array('id' => 2), + 'Comment' => array( + array( + 'id' => 1, + 'comment' => '', + 'published' => 'Y', + 'user_id' => 1, + ), + array( + 'id' => 2, + 'comment' => + 'comment', + 'published' => 'Y', + 'user_id' => 1), + array( + 'id' => 3, + 'comment' => '', + 'published' => 'Y', + 'user_id' => 1 + ))); + $result = $TestModel->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $result = $TestModel->validateAssociated($data); + $this->assertFalse($result); + + $expected = array( + 'Article' => true, + 'Comment' => array(false, true, false) + ); + $result = $TestModel->saveAll($data, array('atomic' => false, 'validate' => 'only')); + $this->assertSame($expected, $result); + $result = $TestModel->validateAssociated($data, array('atomic' => false)); + $this->assertSame($expected, $result); + + $expected = array('Comment' => array( + 0 => array('comment' => array('This field cannot be left blank')), + 2 => array('comment' => array('This field cannot be left blank')) + )); + $this->assertEquals($expected['Comment'], $TestModel->Comment->validationErrors); + + $model = new Comment(); + $model->deleteAll(true); + $model->validate = array('comment' => 'notEmpty'); + $model->Attachment->validate = array('attachment' => 'notEmpty'); + $model->Attachment->bindModel(array('belongsTo' => array('Comment'))); + $expected = array( + 'Comment' => array('comment' => array('This field cannot be left blank')), + 'Attachment' => array('attachment' => array('This field cannot be left blank')) + ); + + $data = array( + 'Comment' => array('comment' => '', 'article_id' => 1, 'user_id' => 1), + 'Attachment' => array('attachment' => '') + ); + $result = $model->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $result = $model->validateAssociated($data); + $this->assertFalse($result); + $this->assertEquals($expected['Comment'], $model->validationErrors); + $this->assertEquals($expected['Attachment'], $model->Attachment->validationErrors); + } + +/** + * testValidateMany method + * + * @return void + */ + public function testValidateMany() { + $TestModel = new Article(); + $TestModel->validate = array('title' => 'notEmpty'); + $data = array( + 0 => array('title' => ''), + 1 => array('title' => 'title 1'), + 2 => array('title' => 'title 2'), + ); + $expected = array( + 0 => array('title' => array('This field cannot be left blank')), + ); + + $result = $TestModel->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $this->assertEquals($expected, $TestModel->validationErrors); + $result = $TestModel->validateMany($data); + $this->assertFalse($result); + $this->assertEquals($expected, $TestModel->validationErrors); + + $data = array( + 0 => array('title' => 'title 0'), + 1 => array('title' => ''), + 2 => array('title' => 'title 2'), + ); + $expected = array( + 1 => array('title' => array('This field cannot be left blank')), + ); + $result = $TestModel->saveAll($data, array('validate' => 'only')); + $this->assertFalse($result); + $this->assertEquals($expected, $TestModel->validationErrors); + $result = $TestModel->validateMany($data); + $this->assertFalse($result); + $this->assertEquals($expected, $TestModel->validationErrors); + } + +/** + * testGetData method + * + * @return void + */ + public function testGetData() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $data = array( + 'Article' => array( + 'id' =>1, 'title' => 'first title' + ), + 'Comment' => array( + array('id' => 1, 'article_id' => 1, 'title' => 'first comment'), + array('id' => 2, 'article_id' => 1, 'title' => 'second comment') + ) + ); + $Validator = new ModelValidator($TestModel); + $TestModel->set($data); + $result = $Validator->getData(null, true); + $this->assertEquals($data, $result); + + $Validator->data = array(); + $result = $Validator->getData(); + $this->assertEquals($data['Article'], $result); + + $result = $Validator->getData('title'); + $this->assertEquals($data['Article']['title'], $result); + } + +/** + * testGetMethods method + * + * @return void + */ + public function testGetMethods() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + + $result = $Validator->getMethods(); + $this->assertEquals(array('model', 'behaviors', 'validator'), array_keys($result)); + + $expected = array_map('strtolower', get_class_methods('Article')); + $this->assertEquals($expected, $result['model']); + } + +/** + * testGetFields method + * + * @return void + */ + public function testGetFields() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + + $result = $Validator->getFields(); + $this->assertSame(array(), $result); + + $Validator->setFields(); + $result = $Validator->getFields(); + $this->assertEquals(array('user_id', 'title','body'), array_keys($result)); + + $result = $Validator->getFields('title'); + $this->assertInstanceOf('CakeField', $result); + } + +/** + * testSetFields method + * + * @return void + */ + public function testSetFields() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $TestModel->whitelist = array('title', 'body'); + $Validator = $TestModel->getValidator(); + + $result = $Validator->setFields(); + $this->assertTrue($result); + + $result = $Validator->getFields(); + $this->assertEquals(array('title','body'), array_keys($result)); + + $result = $Validator->getFields('user_id'); + $this->assertFalse($result); + + $result = $Validator->getFields('body'); + $this->assertEquals(array('notEmpty'), $result->ruleSet); + } + +/** + * testSetOptions method + * + * @return void + */ + public function testSetOptions() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + + $options = array('atomic' => false, 'validate' => true); + $Validator->setOptions($options); + $this->assertEquals($options, $Validator->options); + + $options = array('callbacks' => false); + $Validator->setOptions($options); + $this->assertEquals($options, $Validator->options); + + $options = array('atomic' => true); + $expected = array_merge($Validator->options, $options); + $Validator->setOptions($options, true); + $this->assertEquals($expected, $Validator->options); + } + +/** + * testGetOptions method + * + * @return void + */ + public function testGetOptions() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + $options = array('atomic' => false, 'validate' => true); + $Validator->setOptions($options); + + $result = $Validator->getOptions(); + $this->assertEquals($options, $result); + + $result = $Validator->getOptions('atomic'); + $this->assertFalse($result); + } + +/** + * testSetValidationDomain method + * + * @return void + */ + public function testSetValidationDomain() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + + $result = $Validator->setValidationDomain(); + $this->assertEquals('default', $result->validationDomain); + + $TestModel->validationDomain = 'validation_messages'; + $result = $Validator->setValidationDomain(); + $this->assertEquals('validation_messages', $result->validationDomain); + + $result = $Validator->setValidationDomain('other'); + $this->assertEquals('other', $result->validationDomain); + } + +/** + * testGetModel method + * + * @return void + */ + public function testGetModel() { + $this->loadFixtures('Article', 'Comment'); + $TestModel = new Article(); + $Validator = $TestModel->getValidator(); + + $result = $Validator->getModel(); + $this->assertInstanceOf('Article', $result); + } + } diff --git a/lib/Cake/Test/Case/Model/ModelWriteTest.php b/lib/Cake/Test/Case/Model/ModelWriteTest.php index 76963ebaf..6caed12d0 100644 --- a/lib/Cake/Test/Case/Model/ModelWriteTest.php +++ b/lib/Cake/Test/Case/Model/ModelWriteTest.php @@ -2958,16 +2958,6 @@ class ModelWriteTest extends BaseModelTest { ); $this->assertEquals($expected['Comment'], $model->validationErrors); $this->assertEquals($expected['Attachment'], $model->Attachment->validationErrors); - - $this->assertFalse($model->saveAll( - array( - 'Comment' => array('comment' => '', 'article_id' => 1, 'user_id' => 1), - 'Attachment' => array('attachment' => '') - ), - array('validate' => 'only') - )); - $this->assertEquals($expected['Comment'], $model->validationErrors); - $this->assertEquals($expected['Attachment'], $model->Attachment->validationErrors); } /** @@ -3254,281 +3244,6 @@ class ModelWriteTest extends BaseModelTest { $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 @@ -3676,148 +3391,6 @@ class ModelWriteTest extends BaseModelTest { $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 @@ -4426,59 +3999,6 @@ class ModelWriteTest extends BaseModelTest { $this->assertEquals($errors, $TestModel->validationErrors); } -/** - * testSaveAllValidationOnly method - * - * @return void - */ - public function testSaveAllValidationOnly() { - $this->loadFixtures('Comment', 'Attachment'); - $TestModel = new Comment(); - $TestModel->Attachment->validate = array('attachment' => 'notEmpty'); - - $data = array( - 'Comment' => array( - 'comment' => 'This is the comment' - ), - 'Attachment' => array( - 'attachment' => '' - ) - ); - - $result = $TestModel->saveAll($data, array('validate' => 'only')); - $this->assertFalse($result); - - $TestModel = new Article(); - $TestModel->validate = array('title' => 'notEmpty'); - $result = $TestModel->saveAll( - array( - 0 => array('title' => ''), - 1 => array('title' => 'title 1'), - 2 => array('title' => 'title 2'), - ), - array('validate' => 'only') - ); - $this->assertFalse($result); - $expected = array( - 0 => array('title' => array('This field cannot be left blank')), - ); - $this->assertEquals($expected, $TestModel->validationErrors); - - $result = $TestModel->saveAll( - array( - 0 => array('title' => 'title 0'), - 1 => array('title' => ''), - 2 => array('title' => 'title 2'), - ), - array('validate' => 'only') - ); - $this->assertFalse($result); - $expected = array( - 1 => array('title' => array('This field cannot be left blank')), - ); - $this->assertEquals($expected, $TestModel->validationErrors); - } - /** * testSaveAllValidateFirst method * @@ -4617,82 +4137,6 @@ class ModelWriteTest extends BaseModelTest { $this->assertEquals($expected, $result); } -/** - * testSaveAllHasManyValidationOnly method - * - * @return void - */ - public function testSaveAllHasManyValidationOnly() { - $this->loadFixtures('Article', 'Comment', 'Attachment'); - $TestModel = new Article(); - $TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array(); - $TestModel->Comment->validate = array('comment' => 'notEmpty'); - - $result = $TestModel->saveAll( - array( - 'Article' => array('id' => 2), - 'Comment' => array( - array( - 'id' => 1, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1), - array( - 'id' => 2, - 'comment' => - 'comment', - 'published' => 'Y', - 'user_id' => 1 - ))), - array('validate' => 'only') - ); - $this->assertFalse($result); - - $result = $TestModel->saveAll( - array( - 'Article' => array('id' => 2), - 'Comment' => array( - array( - 'id' => 1, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1 - ), - array( - 'id' => 2, - 'comment' => 'comment', - 'published' => 'Y', - 'user_id' => 1 - ), - array( - 'id' => 3, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1 - ))), - array( - 'validate' => 'only', - 'atomic' => false - )); - $expected = array( - 'Article' => true, - 'Comment' => array(false, true, false) - ); - $this->assertSame($expected, $result); - - $expected = array('Comment' => array( - 0 => array('comment' => array('This field cannot be left blank')), - 2 => array('comment' => array('This field cannot be left blank')) - )); - $this->assertEquals($expected, $TestModel->validationErrors); - - $expected = array( - 0 => array('comment' => array('This field cannot be left blank')), - 2 => array('comment' => array('This field cannot be left blank')) - ); - $this->assertEquals($expected, $TestModel->Comment->validationErrors); - } - /** * test that saveAll behaves like plain save() when supplied empty data * @@ -5773,39 +5217,6 @@ class ModelWriteTest extends BaseModelTest { $this->assertEquals($errors, $TestModel->validationErrors); } -/** - * testValidateMany method - * - * @return void - */ - public function testValidateMany() { - $TestModel = new Article(); - $TestModel->validate = array('title' => 'notEmpty'); - $result = $TestModel->validateMany( - array( - 0 => array('title' => ''), - 1 => array('title' => 'title 1'), - 2 => array('title' => 'title 2'), - )); - $this->assertFalse($result); - $expected = array( - 0 => array('title' => array('This field cannot be left blank')), - ); - $this->assertEquals($expected, $TestModel->validationErrors); - - $result = $TestModel->validateMany( - array( - 0 => array('title' => 'title 0'), - 1 => array('title' => ''), - 2 => array('title' => 'title 2'), - )); - $this->assertFalse($result); - $expected = array( - 1 => array('title' => array('This field cannot be left blank')), - ); - $this->assertEquals($expected, $TestModel->validationErrors); - } - /** * testSaveAssociatedValidateFirst method * @@ -5943,93 +5354,6 @@ class ModelWriteTest extends BaseModelTest { $this->assertEquals($expected, $result); } -/** - * testValidateAssociated method - * - * @return void - */ - public function testValidateAssociated() { - $TestModel = new Comment(); - $TestModel->Attachment->validate = array('attachment' => 'notEmpty'); - - $data = array( - 'Comment' => array( - 'comment' => 'This is the comment' - ), - 'Attachment' => array( - 'attachment' => '' - ) - ); - - $result = $TestModel->validateAssociated($data); - $this->assertFalse($result); - - $TestModel = new Article(); - $TestModel->belongsTo = $TestModel->hasAndBelongsToMany = array(); - $TestModel->Comment->validate = array('comment' => 'notEmpty'); - - $result = $TestModel->validateAssociated( - array( - 'Article' => array('id' => 2), - 'Comment' => array( - array( - 'id' => 1, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1), - array( - 'id' => 2, - 'comment' => - 'comment', - 'published' => 'Y', - 'user_id' => 1 - )))); - $this->assertFalse($result); - - $result = $TestModel->validateAssociated( - array( - 'Article' => array('id' => 2), - 'Comment' => array( - array( - 'id' => 1, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1 - ), - array( - 'id' => 2, - 'comment' => 'comment', - 'published' => 'Y', - 'user_id' => 1 - ), - array( - 'id' => 3, - 'comment' => '', - 'published' => 'Y', - 'user_id' => 1 - ))), - array( - 'atomic' => false - )); - $expected = array( - 'Article' => true, - 'Comment' => array(false, true, false) - ); - $this->assertSame($expected, $result); - - $expected = array('Comment' => array( - 0 => array('comment' => array('This field cannot be left blank')), - 2 => array('comment' => array('This field cannot be left blank')) - )); - $this->assertEquals($expected, $TestModel->validationErrors); - - $expected = array( - 0 => array('comment' => array('This field cannot be left blank')), - 2 => array('comment' => array('This field cannot be left blank')) - ); - $this->assertEquals($expected, $TestModel->Comment->validationErrors); - } - /** * test that saveMany behaves like plain save() when suplied empty data * @@ -6478,42 +5802,6 @@ class ModelWriteTest extends BaseModelTest { $this->assertEquals('', $result[0]['Comment'][1]['comment']); } -/** - * testSaveAllFieldListHasOne method - * - * @return void - */ - public function testSaveAllFieldListHasOne() { - $this->loadFixtures('Attachment', 'Comment', 'Article', 'User'); - $TestModel = new Comment(); - - $TestModel->validate = array('comment' => 'notEmpty'); - $TestModel->Attachment->validate = array('attachment' => 'notEmpty'); - - $record = array( - 'Comment' => array( - 'user_id' => 1, - 'article_id' => 1, - 'comment' => '', - ), - 'Attachment' => array( - 'attachment' => '' - ) - ); - $result = $TestModel->saveAll($record, array('validate' => 'only')); - $this->assertFalse($result); - - $fieldList = array( - 'Comment' => array('id', 'article_id', 'user_id'), - 'Attachment' => array('comment_id') - ); - $result = $TestModel->saveAll($record, array( - 'fieldList' => $fieldList, 'validate' => 'only' - )); - $this->assertTrue($result); - $this->assertEmpty($TestModel->validationErrors); - } - /** * testSaveAllDeepFieldListValidateBelongsTo * diff --git a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php new file mode 100644 index 000000000..9774857e9 --- /dev/null +++ b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php @@ -0,0 +1,181 @@ + + * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package Cake.Test.Case.Model.Validator + * @since CakePHP(tm) v 2.2.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +require_once dirname(dirname(__FILE__)) . DS . 'ModelTestBase.php'; + +/** + * CakeFieldTest + * + * @package Cake.Test.Case.Model.Validator + */ +class CakeFieldTest extends BaseModelTest { + +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + $this->Article = new Article(); + $this->Article->set(array('title' => '', 'body' => 'no title')); + $this->Validator = new ModelValidator($this->Article); + $this->Validator->getData(); + } + +/** + * testConstruct method + * + * @return void + */ + public function testConstruct() { + $Field = new CakeField($this->Validator, 'title', 'notEmpty'); + + $this->assertEquals(array('title' => '', 'body' => 'no title'), $Field->data); + $this->assertEquals('title', $Field->field); + $this->assertEquals(array('notEmpty'), $Field->ruleSet); + } + +/** + * testValidate method + * + * @return void + */ + public function testValidate() { + $Field = new CakeField($this->Validator, 'title', 'notEmpty'); + + $result = $Field->validate(); + $this->assertFalse($result); + + $Field = new CakeField($this->Validator, 'body', 'notEmpty'); + + $result = $Field->validate(); + $this->assertTrue($result); + + $Field = new CakeField($this->Validator, 'nothere', array('notEmpty' => array('rule' => 'notEmpty', 'required' => true))); + + $result = $Field->validate(); + $this->assertFalse($result); + } + +/** + * testGetRule method + * + * @return void + */ + public function testGetRule() { + $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); + $Field = new CakeField($this->Validator, 'title', $rules); + + $result = $Field->getRule('notEmpty'); + $this->assertInstanceOf('CakeRule', $result); + $this->assertEquals('notEmpty', $result->rule); + $this->assertEquals(null, $result->required); + $this->assertEquals(false, $result->allowEmpty); + $this->assertEquals(null, $result->on); + $this->assertEquals(true, $result->last); + $this->assertEquals('Can not be empty', $result->message); + $this->assertEquals(array('title' => '', 'body' => 'no title'), $result->data); + } + +/** + * testGetRules method + * + * @return void + */ + public function testGetRules() { + $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); + $Field = new CakeField($this->Validator, 'title', $rules); + + $result = $Field->getRules(); + $this->assertEquals(array('notEmpty'), array_keys($result)); + $this->assertInstanceOf('CakeRule', $result['notEmpty']); + } + +/** + * testSetRule method + * + * @return void + */ + public function testSetRule() { + $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); + $Field = new CakeField($this->Validator, 'title', $rules); + $Rule = new CakeRule($Field, $rules['notEmpty'], 'notEmpty'); + + $this->assertEquals($Rule, $Field->getRule('notEmpty')); + + $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); + $Rule = new CakeRule($Field, $rules['validEmail'], 'validEmail'); + $Field->setRule('validEmail', $Rule); + $result = $Field->getRules(); + $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); + + $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Other message')); + $Rule = new CakeRule($Field, $rules['validEmail'], 'validEmail'); + $Field->setRule('validEmail', $Rule); + $result = $Field->getRules(); + $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); + $result = $Field->getRule('validEmail'); + $this->assertInstanceOf('CakeRule', $result); + $this->assertEquals('email', $result->rule); + $this->assertEquals(null, $result->required); + $this->assertEquals(false, $result->allowEmpty); + $this->assertEquals(null, $result->on); + $this->assertEquals(true, $result->last); + $this->assertEquals('Other message', $result->message); + $this->assertEquals(array('title' => '', 'body' => 'no title'), $result->data); + } + +/** + * testSetRules method + * + * @return void + */ + public function testSetRules() { + $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); + $Field = new CakeField($this->Validator, 'title', $rule); + $RuleEmpty = new CakeRule($Field, $rule['notEmpty'], 'notEmpty'); + + $rule = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); + $RuleEmail = new CakeRule($Field, $rule['validEmail'], 'validEmail'); + + $rules = array('validEmail' => $RuleEmail); + $Field->setRules($rules, false); + $result = $Field->getRules(); + $this->assertEquals(array('validEmail'), array_keys($result)); + + $rules = array('notEmpty' => $RuleEmpty); + $Field->setRules($rules, true); + $result = $Field->getRules(); + $this->assertEquals(array('validEmail', 'notEmpty'), array_keys($result)); + } + +/** + * testGetValidator method + * + * @return void + */ + public function testGetValidator() { + $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); + $Field = new CakeField($this->Validator, 'title', $rule); + $result = $Field->getValidator(); + $this->assertInstanceOf('ModelValidator', $result); + } + +} diff --git a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php new file mode 100644 index 000000000..e95013701 --- /dev/null +++ b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php @@ -0,0 +1,52 @@ + + * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * Redistributions of files must retain the above copyright notice + * + * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @link http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests + * @package Cake.Test.Case.Model.Validator + * @since CakePHP(tm) v 2.2.0 + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +require_once dirname(dirname(__FILE__)) . DS . 'ModelTestBase.php'; + +/** + * CakeRuleTest + * + * @package Cake.Test.Case.Model.Validator + */ +class CakeRuleTest extends BaseModelTest { + +/** + * setUp method + * + * @return void + */ + public function setUp() { + parent::setUp(); + $Article = new Article(); + $Article->set(array('title' => '', 'body' => 'no title')); + $this->Validator = new ModelValidator($Article); + $this->Validator->getData(); + $rule = array('notEmpty' => array('rule' => 'notEmpty', 'required' => true, 'last' => false)); + $this->Field = new CakeField($this->Validator, 'body', $rule); + } + +/** + * testIsValid method + * + * @return void + */ + public function testIsValid() { + + } +} From 8eb02d0539a0e59a2bd28194ef82e9f153ceafba Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 19:20:22 -0430 Subject: [PATCH 03/42] Simplifying options for setting/getting a model validator. Adding loading support for validators is was simplified into requiring custom validators to extend core ModelValidator --- lib/Cake/Model/Model.php | 89 ++++++------------------------- lib/Cake/Model/ModelValidator.php | 7 --- 2 files changed, 16 insertions(+), 80 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 3dd85bc81..eddb34b78 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -1454,7 +1454,7 @@ class Model extends Object implements CakeEventListener { $defaults = array(); $this->id = false; $this->data = array(); - $this->validationErrors = $this->getValidator()->validationErrors = array(); + $this->validationErrors = $this->validator()->validationErrors = array(); if ($data !== null && $data !== false) { foreach ($this->schema() as $field => $properties) { @@ -2117,7 +2117,7 @@ class Model extends Object implements CakeEventListener { * depending on whether each record validated successfully. */ public function validateMany($data, $options = array()) { - return $this->getValidator()->validateMany($data, $options); + return $this->validator()->validateMany($data, $options); } /** @@ -2292,7 +2292,7 @@ class Model extends Object implements CakeEventListener { * depending on whether each record validated successfully. */ public function validateAssociated($data, $options = array()) { - return $this->getValidator()->validateAssociated($data, $options); + return $this->validator()->validateAssociated($data, $options); } /** @@ -2979,7 +2979,7 @@ class Model extends Object implements CakeEventListener { * @return boolean True if there are no errors */ public function validates($options = array()) { - return $this->getValidator()->validates($options); + return $this->validator()->validates($options); } /** @@ -2990,7 +2990,7 @@ class Model extends Object implements CakeEventListener { * @see Model::validates() */ public function invalidFields($options = array()) { - return $this->getValidator()->invalidFields($options); + return $this->validator()->invalidFields($options); } /** @@ -3003,7 +3003,7 @@ class Model extends Object implements CakeEventListener { * @return void */ public function invalidate($field, $value = true) { - $this->getValidator()->invalidate($field, $value); + $this->validator()->invalidate($field, $value); } /** @@ -3351,77 +3351,20 @@ class Model extends Object implements CakeEventListener { } /** - * Creates a ModelValidator instance from Model::validatorClass - * - * @return void - * @throws MissingValidatorException - * @throws InvalidValidatorException - */ - public function setValidator($validator = null) { - if (is_object($validator) && $this->_isValidValidator($validator)) { - $this->_validator = $validator; - return $this; - } - - if (is_null($validator) && is_null($this->validatorClass)) { - $this->validatorClass = ModelValidator::DEFAULT_VALIDATOR; - } elseif (is_string($validator)) { - $this->validatorClass = $validator; - } - - if (!$this->_loadValidator($this->validatorClass)) { - throw new MissingValidatorException(array($this->validatorClass)); - } - - if (!$this->_isValidValidator($this->_validator)) { - $this->_validator = null; - throw new InvalidValidatorException(array($this->validatorClass, ModelValidator::DEFAULT_VALIDATOR)); - } - - return $this; - } - -/** - * Returns the currently set ModelValidator instance + * Retunrs an instance of a model validator for this class * * @return ModelValidator */ - public function getValidator() { + public function validator($instance = null) { + if ($validator instanceof ModelValidator) { + return $this->_validator = $validator; + } + + if (is_null($validator) && is_null($this->validatorClass)) { + $this->_validator = new ModelValidator($this); + } + return $this->_validator; } -/** - * Tries to load a validator and returns true if the class could be found, false otherwise. - * - * @param string $validatorClass The class to be loaded - * @return boolean True if the class was found, false otherwise - */ - protected function _loadValidator($validatorClass) { - list($plugin, $class) = pluginSplit($validatorClass, true); - unset($validatorClass); - - $location = $plugin . 'Model'; - App::uses($class, $location); - - if (!class_exists($class, true)) { - return false; - } - $this->_validator = new $class($this); - - return true; - } - -/** - * Checks if the passed in validator instance is either an instance or subclass of ModelValidator. - * - * @param $validator - * @return boolean True if the instance is valid, false otherwise - */ - protected function _isValidValidator($validator) { - if (!($validator instanceof ModelValidator) && !is_subclass_of($validator, ModelValidator::DEFAULT_VALIDATOR)) { - return false; - } - return true; - } - } diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 6b92f969e..12f0299df 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -29,13 +29,6 @@ App::uses('CakeRule', 'Model/Validator'); */ class ModelValidator { -/** - * The default ModelValidator class name - * - * @var string - */ - const DEFAULT_VALIDATOR = 'ModelValidator'; - /** * The default validation domain * From f54eb0cdeef17ee637e1145b7a1ecd60fc999899 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 19:38:18 -0430 Subject: [PATCH 04/42] following coding standards and simplifying some logic --- lib/Cake/Model/ModelValidator.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 12f0299df..bee9b432f 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -241,17 +241,18 @@ class ModelValidator { if (!$this->propagateBeforeValidate($options)) { return false; } + $model = $this->getModel(); $this->data = array(); $this->setOptions($options); if (!$this->setFields()) { - return $this->getModel()->validationErrors = $this->validationErrors; + return $model->validationErrors = $this->validationErrors; } $this->getData(); $this->getMethods(); - $this->setValidationDomain(); + $this->setValidationDomain($model->validationDomain); foreach ($this->_fields as $field) { $field->validate(); @@ -350,7 +351,7 @@ class ModelValidator { } /** - * Sets the CakeField isntances from the Model::$validate property after processing the fieldList and whiteList. + * Sets the CakeField instances from the Model::$validate property after processing the fieldList and whiteList. * If Model::$validate is not set or empty, this method returns false. True otherwise. * * @param boolean $reset If true will reset the Validator $validate array to the Model's default @@ -413,10 +414,10 @@ class ModelValidator { * @param string $name The options name to look up * @return mixed Either null or the option value */ - public function getOptions($name = NULL) { - if (NULL !== $name) { + public function getOptions($name = null) { + if ($name !== null) { if (!isset($this->options[$name])) { - return NULL; + return null; } return $this->options[$name]; } @@ -432,8 +433,6 @@ class ModelValidator { public function setValidationDomain($validationDomain = null) { if ($validationDomain !== null) { $this->validationDomain = $validationDomain; - } elseif ($this->_model->validationDomain !== null) { - $this->validationDomain = $this->_model->validationDomain; } else { $this->validationDomain = ModelValidator::DEFAULT_DOMAIN; } @@ -471,7 +470,7 @@ class ModelValidator { if (!empty($whitelist)) { $this->validationErrors = array(); $validate = array(); - foreach ((array) $whitelist as $f) { + foreach ((array)$whitelist as $f) { if (!empty($this->_validate[$f])) { $validate[$f] = $this->_validate[$f]; } From 3ea2d7bca5ef123f475d09614a68c9d6392d9179 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 20:04:33 -0430 Subject: [PATCH 05/42] Removing validatior construction in model constructor --- lib/Cake/Model/Model.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index eddb34b78..51d85803b 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -731,7 +731,6 @@ class Model extends Object implements CakeEventListener { $this->_createLinks(); $this->Behaviors->init($this->alias, $this->actsAs); - $this->setValidator(); } /** From 4519ba1221082fe2a735f74645ed596af462ebe3 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 20:05:41 -0430 Subject: [PATCH 06/42] Fixed variable reference --- lib/Cake/Model/Model.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 51d85803b..2cbabaf18 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -3355,11 +3355,11 @@ class Model extends Object implements CakeEventListener { * @return ModelValidator */ public function validator($instance = null) { - if ($validator instanceof ModelValidator) { - return $this->_validator = $validator; + if ($instance instanceof ModelValidator) { + return $this->_validator = $instance; } - if (is_null($validator) && is_null($this->validatorClass)) { + if (is_null($instance) && is_null($this->validatorClass)) { $this->_validator = new ModelValidator($this); } From 573d292ffabf9afa9d6d8733af46d66bf63099a0 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 20:09:29 -0430 Subject: [PATCH 07/42] Fixed more refereces to old method --- lib/Cake/Model/ModelValidator.php | 10 +-- .../Test/Case/Model/ModelValidationTest.php | 70 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index bee9b432f..e8b2d8f56 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -152,9 +152,9 @@ class ModelValidator { if (isset($associations[$association])) { if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { if ($options['deep']) { - $validates = $this->getModel()->{$association}->getValidator()->validateAssociated($values, $options); + $validates = $this->getModel()->{$association}->validator()->validateAssociated($values, $options); } else { - $validates = $this->getModel()->{$association}->create($values) !== null && $this->getModel()->{$association}->getValidator()->validates($options); + $validates = $this->getModel()->{$association}->create($values) !== null && $this->getModel()->{$association}->validator()->validates($options); } if (is_array($validates)) { if (in_array(false, $validates, true)) { @@ -165,11 +165,11 @@ class ModelValidator { } $return[$association] = $validates; } elseif ($associations[$association] === 'hasMany') { - $validates = $this->getModel()->{$association}->getValidator()->validateMany($values, $options); + $validates = $this->getModel()->{$association}->validator()->validateMany($values, $options); $return[$association] = $validates; } if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $this->validationErrors[$association] = $this->getModel()->{$association}->getValidator()->validationErrors; + $this->validationErrors[$association] = $this->getModel()->{$association}->validator()->validationErrors; } } } @@ -512,7 +512,7 @@ class ModelValidator { foreach ($newData as $data) { $data[$this->getModel()->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->getModel()->id; $this->getModel()->{$join}->create($data); - $valid = ($valid && $this->getModel()->{$join}->getValidator()->validates($options)); + $valid = ($valid && $this->getModel()->{$join}->validator()->validates($options)); } } return $valid; diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index b828f08b6..f672e87c5 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -190,7 +190,7 @@ class ModelValidationTest extends BaseModelTest { */ public function testInvalidFieldsWhitelist() { $TestModel = new ValidationTest1(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $TestModel->validate = array( 'title' => array( 'rule' => 'alphaNumeric', @@ -648,7 +648,7 @@ class ModelValidationTest extends BaseModelTest { $Something = new Something(); $JoinThing = $Something->JoinThing; - $Validator = $Something->JoinThing->getValidator(); + $Validator = $Something->JoinThing->validator(); $JoinThing->validate = array('doomed' => array('rule' => 'notEmpty')); @@ -713,15 +713,15 @@ class ModelValidationTest extends BaseModelTest { $result = $Something->validateAssociated($data); $this->assertFalse($result); $this->assertEquals($expectedError, $JoinThing->validationErrors); - $result = $Something->getValidator()->validateAssociated($data); + $result = $Something->validator()->validateAssociated($data); $this->assertFalse($result); - $this->assertEquals($expectedError, $JoinThing->getValidator()->validationErrors); + $this->assertEquals($expectedError, $JoinThing->validator()->validationErrors); $Something->create(); $result = $Something->saveAll($data, array('validate' => 'first')); $this->assertFalse($result); $this->assertEquals($expectedError, $JoinThing->validationErrors); - $this->assertEquals($expectedError, $JoinThing->getValidator()->validationErrors); + $this->assertEquals($expectedError, $JoinThing->validator()->validationErrors); $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); $this->assertSame($count, 0); @@ -760,7 +760,7 @@ class ModelValidationTest extends BaseModelTest { $this->assertTrue($result); $result = $Author->validateAssociated($data); $this->assertTrue($result); - $result = $Author->getValidator()->validateAssociated($data); + $result = $Author->validator()->validateAssociated($data); $this->assertTrue($result); $Author->create(); @@ -816,8 +816,8 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->invalidFields(array('fieldList' => array('title'))); $this->assertEquals(array(), $TestModel->validationErrors); - $TestModel->getValidator()->invalidFields(array('fieldList' => array('title'))); - $this->assertEquals(array(), $TestModel->getValidator()->validationErrors); + $TestModel->validator()->invalidFields(array('fieldList' => array('title'))); + $this->assertEquals(array(), $TestModel->validator()->validationErrors); } /** @@ -851,9 +851,9 @@ class ModelValidationTest extends BaseModelTest { $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->getValidator()->validationErrors = array(); - $TestModel->getValidator()->invalidFields(); - $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); + $TestModel->validator()->validationErrors = array(); + $TestModel->validator()->invalidFields(); + $this->assertEquals($expected, $TestModel->validator()->validationErrors); $TestModel->create(array('title' => 'foo')); $expected = array( @@ -865,9 +865,9 @@ class ModelValidationTest extends BaseModelTest { $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->getValidator()->validationErrors = array(); - $TestModel->getValidator()->invalidFields(); - $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); + $TestModel->validator()->validationErrors = array(); + $TestModel->validator()->invalidFields(); + $this->assertEquals($expected, $TestModel->validator()->validationErrors); } /** @@ -903,9 +903,9 @@ class ModelValidationTest extends BaseModelTest { $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->getValidator()->validationErrors = array(); - $TestModel->getValidator()->invalidFields(); - $this->assertEquals($expected, $TestModel->getValidator()->validationErrors); + $TestModel->validator()->validationErrors = array(); + $TestModel->validator()->invalidFields(); + $this->assertEquals($expected, $TestModel->validator()->validationErrors); $TestModel->validationDomain = 'default'; Configure::write('Config.language', $lang); @@ -940,13 +940,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->getValidator()->validates()); + $this->assertFalse($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->getValidator()->validates()); + $this->assertTrue($Article->validator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -960,13 +960,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->getValidator()->validates()); + $this->assertTrue($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->getValidator()->validates()); + $this->assertFalse($Article->validator()->validates()); } /** @@ -997,13 +997,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->getValidator()->validates()); + $this->assertFalse($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->getValidator()->validates()); + $this->assertTrue($Article->validator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -1017,13 +1017,13 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->getValidator()->validates()); + $this->assertTrue($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->getValidator()->validates()); + $this->assertFalse($Article->validator()->validates()); } /** @@ -1175,7 +1175,7 @@ class ModelValidationTest extends BaseModelTest { $this->assertTrue($result); $result = $TestModel->validateAssociated($data, array('deep' => true)); $this->assertTrue($result); - $result = $TestModel->getValidator()->validateAssociated($data, array('deep' => true)); + $result = $TestModel->validator()->validateAssociated($data, array('deep' => true)); $this->assertTrue($result); $data = array( @@ -1189,7 +1189,7 @@ class ModelValidationTest extends BaseModelTest { $this->assertFalse($result); $result = $TestModel->validateAssociated($data, array('deep' => true)); $this->assertFalse($result); - $result = $TestModel->getValidator()->validateAssociated($data, array('deep' => true)); + $result = $TestModel->validator()->validateAssociated($data, array('deep' => true)); $this->assertFalse($result); $data = array( @@ -1210,7 +1210,7 @@ class ModelValidationTest extends BaseModelTest { $this->assertSame($expected, $result); $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); $this->assertSame($expected, $result); - $result = $TestModel->getValidator()->validateAssociated($data, array('atomic' => false, 'deep' => true)); + $result = $TestModel->validator()->validateAssociated($data, array('atomic' => false, 'deep' => true)); $this->assertSame($expected, $result); $data = array( @@ -1805,7 +1805,7 @@ class ModelValidationTest extends BaseModelTest { public function testGetMethods() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $result = $Validator->getMethods(); $this->assertEquals(array('model', 'behaviors', 'validator'), array_keys($result)); @@ -1822,7 +1822,7 @@ class ModelValidationTest extends BaseModelTest { public function testGetFields() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $result = $Validator->getFields(); $this->assertSame(array(), $result); @@ -1844,7 +1844,7 @@ class ModelValidationTest extends BaseModelTest { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); $TestModel->whitelist = array('title', 'body'); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $result = $Validator->setFields(); $this->assertTrue($result); @@ -1867,7 +1867,7 @@ class ModelValidationTest extends BaseModelTest { public function testSetOptions() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $options = array('atomic' => false, 'validate' => true); $Validator->setOptions($options); @@ -1891,7 +1891,7 @@ class ModelValidationTest extends BaseModelTest { public function testGetOptions() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $options = array('atomic' => false, 'validate' => true); $Validator->setOptions($options); @@ -1910,7 +1910,7 @@ class ModelValidationTest extends BaseModelTest { public function testSetValidationDomain() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $result = $Validator->setValidationDomain(); $this->assertEquals('default', $result->validationDomain); @@ -1931,7 +1931,7 @@ class ModelValidationTest extends BaseModelTest { public function testGetModel() { $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); - $Validator = $TestModel->getValidator(); + $Validator = $TestModel->validator(); $result = $Validator->getModel(); $this->assertInstanceOf('Article', $result); From b83f936e835953578248b777f0eba723a385755f Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 28 Apr 2012 23:25:05 -0430 Subject: [PATCH 08/42] General refactor on ModelValidation to decrease class coupling and increase readability --- lib/Cake/Model/ModelValidator.php | 166 +++++++++--------------- lib/Cake/Model/Validator/CakeField.php | 129 +++++++++++++------ lib/Cake/Model/Validator/CakeRule.php | 167 +++++++------------------ 3 files changed, 194 insertions(+), 268 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index e8b2d8f56..c90636fcf 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -29,27 +29,6 @@ App::uses('CakeRule', 'Model/Validator'); */ class ModelValidator { -/** - * The default validation domain - * - * @var string - */ - const DEFAULT_DOMAIN = 'default'; - -/** - * Holds the data array from the Model - * - * @var array - */ - public $data = array(); - -/** - * The default ValidationDomain - * - * @var string - */ - public $validationDomain = 'default'; - /** * Holds the validationErrors * @@ -139,22 +118,24 @@ class ModelValidator { */ public function validateAssociated($data, $options = array()) { $options = array_merge(array('atomic' => true, 'deep' => false), $options); - $this->validationErrors = $this->getModel()->validationErrors = $return = array(); - if (!($this->getModel()->create($data) && $this->validates($options))) { - $this->validationErrors = array($this->getModel()->alias => $this->validationErrors); - $return[$this->getModel()->alias] = false; + $model = $this->getModel(); + + $this->validationErrors = $model->validationErrors = $return = array(); + if (!($model->create($data) && $this->validates($options))) { + $this->validationErrors = array($model->alias => $this->validationErrors); + $return[$model->alias] = false; } else { - $return[$this->getModel()->alias] = true; + $return[$model->alias] = true; } - $associations = $this->getModel()->getAssociated(); + $associations = $model->getAssociated(); foreach ($data as $association => $values) { $validates = true; if (isset($associations[$association])) { if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { if ($options['deep']) { - $validates = $this->getModel()->{$association}->validator()->validateAssociated($values, $options); + $validates = $model->{$association}->validator()->validateAssociated($values, $options); } else { - $validates = $this->getModel()->{$association}->create($values) !== null && $this->getModel()->{$association}->validator()->validates($options); + $validates = $model->{$association}->create($values) !== null && $model->{$association}->validator()->validates($options); } if (is_array($validates)) { if (in_array(false, $validates, true)) { @@ -165,23 +146,23 @@ class ModelValidator { } $return[$association] = $validates; } elseif ($associations[$association] === 'hasMany') { - $validates = $this->getModel()->{$association}->validator()->validateMany($values, $options); + $validates = $model->{$association}->validator()->validateMany($values, $options); $return[$association] = $validates; } if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $this->validationErrors[$association] = $this->getModel()->{$association}->validator()->validationErrors; + $this->validationErrors[$association] = $model->{$association}->validator()->validationErrors; } } } - if (isset($this->validationErrors[$this->getModel()->alias])) { - $this->validationErrors = $this->validationErrors[$this->getModel()->alias]; + if (isset($this->validationErrors[$model->alias])) { + $this->validationErrors = $this->validationErrors[$model->alias]; } - $this->getModel()->validationErrors = $this->validationErrors; + $model->validationErrors = $this->validationErrors; if (!$options['atomic']) { return $return; } - if ($return[$this->getModel()->alias] === false || !empty($this->validationErrors)) { + if ($return[$model->alias] === false || !empty($this->validationErrors)) { return false; } return true; @@ -205,12 +186,14 @@ class ModelValidator { */ public function validateMany($data, $options = array()) { $options = array_merge(array('atomic' => true, 'deep' => false), $options); - $this->validationErrors = $validationErrors = $this->getModel()->validationErrors = $return = array(); + $model = $this->getModel(); + + $this->validationErrors = $validationErrors = $model->validationErrors = $return = array(); foreach ($data as $key => $record) { if ($options['deep']) { $validates = $this->validateAssociated($record, $options); } else { - $validates = $this->getModel()->create($record) && $this->validates($options); + $validates = $model->create($record) && $this->validates($options); } if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) { $validationErrors[$key] = $this->validationErrors; @@ -220,7 +203,7 @@ class ModelValidator { } $return[$key] = $validates; } - $this->validationErrors = $this->getModel()->validationErrors = $validationErrors; + $this->validationErrors = $model->validationErrors = $validationErrors; if (!$options['atomic']) { return $return; } @@ -242,25 +225,24 @@ class ModelValidator { return false; } $model = $this->getModel(); - $this->data = array(); - $this->setOptions($options); - if (!$this->setFields()) { + if (!$this->_parseRules()) { return $model->validationErrors = $this->validationErrors; } - $this->getData(); - $this->getMethods(); - $this->setValidationDomain($model->validationDomain); - + $exists = $model->exists(); + $methods = $this->getMethods(); foreach ($this->_fields as $field) { - $field->validate(); + $field->setMethods($methods); + $field->setValidationDomain($model->validationDomain); + $errors = $field->validate($model->data, $exists); + foreach ($errors as $error) { + $this->invalidate($field->field, $error); + } } - $this->setFields(true); - - return $this->getModel()->validationErrors = $this->validationErrors; + return $model->validationErrors = $this->validationErrors; } /** @@ -279,34 +261,6 @@ class ModelValidator { $this->validationErrors[$field][] = $this->getModel()->validationErrors[$field][] = $value; } -/** - * Gets the current data from the model and sets it to $this->data - * - * @param string $field [optional] - * @return array The data - */ - public function getData($field = null, $all = false) { - if (!empty($this->data)) { - if ($field !== null && isset($this->data[$field])) { - return $this->data[$field]; - } - return $this->data; - } - - $this->data = $this->_model->data; - if (FALSE === $all && isset($this->data[$this->_model->alias])) { - $this->data = $this->data[$this->_model->alias]; - } elseif (!is_array($this->data)) { - $this->data = array(); - } - - if ($field !== null && isset($this->data[$field])) { - return $this->data[$field]; - } - - return $this->data; - } - /** * Gets all possible custom methods from the Model, Behaviors and the Validator. * If $type is null (default) gets all methods. If $type is one of 'model', 'behaviors' or 'validator', @@ -354,28 +308,27 @@ class ModelValidator { * Sets the CakeField instances from the Model::$validate property after processing the fieldList and whiteList. * If Model::$validate is not set or empty, this method returns false. True otherwise. * - * @param boolean $reset If true will reset the Validator $validate array to the Model's default * @return boolean True if Model::$validate was processed, false otherwise */ - public function setFields($reset = false) { - if (!isset($this->_model->validate) || empty($this->_model->validate)) { + protected function _parseRules() { + if (empty($this->_model->validate)) { $this->_validate = array(); + $this->_fields = array(); return false; } - $this->_validate = $this->_model->validate; - - if ($reset === true) { + if (!empty($this->_validate) && $this->_validate === $this->_model->validate) { return true; } + $this->_validate = $this->_model->validate; $this->_processWhitelist(); $this->_fields = array(); + $methods = $this->getMethods(); foreach ($this->_validate as $fieldName => $ruleSet) { - $this->_fields[$fieldName] = new CakeField($this, $fieldName, $ruleSet); + $this->_fields[$fieldName] = new CakeField($fieldName, $ruleSet, $methods); } - unset($fieldName, $ruleSet); return true; } @@ -427,16 +380,11 @@ class ModelValidator { /** * Sets the I18n domain for validation messages. This method is chainable. * - * @param string $validationDomain [optional] The validation domain to be used. If none is given, uses Model::$validationDomain + * @param string $validationDomain [optional] The validation domain to be used. * @return ModelValidator */ - public function setValidationDomain($validationDomain = null) { - if ($validationDomain !== null) { - $this->validationDomain = $validationDomain; - } else { - $this->validationDomain = ModelValidator::DEFAULT_DOMAIN; - } - + public function setValidationDomain($validationDomain) { + $model->validationDomain = $validationDomain; return $this; } @@ -455,12 +403,13 @@ class ModelValidator { * @return void */ protected function _processWhitelist() { - $whitelist = $this->getModel()->whitelist; + $model = $this->getModel(); + $whitelist = $model->whitelist; $fieldList = $this->getOptions('fieldList'); if (!empty($fieldList)) { - if (!empty($fieldList[$this->getModel()->alias]) && is_array($fieldList[$this->getModel()->alias])) { - $whitelist = $fieldList[$this->getModel()->alias]; + if (!empty($fieldList[$model->alias]) && is_array($fieldList[$model->alias])) { + $whitelist = $fieldList[$model->alias]; } else { $whitelist = $fieldList; } @@ -489,20 +438,20 @@ class ModelValidator { */ protected function _validateWithModels($options) { $valid = true; - $this->getData(null, true); + $model = $this->getModel(); - foreach ($this->getModel()->hasAndBelongsToMany as $assoc => $association) { - if (empty($association['with']) || !isset($this->data[$assoc])) { + foreach ($model->hasAndBelongsToMany as $assoc => $association) { + if (empty($association['with']) || !isset($model->data[$assoc])) { continue; } - list($join) = $this->getModel()->joinModel($this->getModel()->hasAndBelongsToMany[$assoc]['with']); - $data = $this->data[$assoc]; + list($join) = $model->joinModel($model->hasAndBelongsToMany[$assoc]['with']); + $data = $model->data[$assoc]; $newData = array(); foreach ((array)$data as $row) { - if (isset($row[$this->getModel()->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + if (isset($row[$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { $newData[] = $row; - } elseif (isset($row[$join]) && isset($row[$join][$this->getModel()->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { + } elseif (isset($row[$join]) && isset($row[$join][$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) { $newData[] = $row[$join]; } } @@ -510,9 +459,9 @@ class ModelValidator { continue; } foreach ($newData as $data) { - $data[$this->getModel()->hasAndBelongsToMany[$assoc]['foreignKey']] = $this->getModel()->id; - $this->getModel()->{$join}->create($data); - $valid = ($valid && $this->getModel()->{$join}->validator()->validates($options)); + $data[$model->hasAndBelongsToMany[$assoc]['foreignKey']] = $model->id; + $model->{$join}->create($data); + $valid = ($valid && $model->{$join}->validator()->validates($options)); } } return $valid; @@ -525,9 +474,10 @@ class ModelValidator { * @return boolean */ public function propagateBeforeValidate($options = array()) { - $event = new CakeEvent('Model.beforeValidate', $this->getModel(), array($options)); + $model = $this->getModel(); + $event = new CakeEvent('Model.beforeValidate', $model, array($options)); list($event->break, $event->breakOn) = array(true, false); - $this->getModel()->getEventManager()->dispatch($event); + $model->getEventManager()->dispatch($event); if ($event->isStopped()) { return false; } diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php index 9bd6fea60..311c2b386 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeField.php @@ -29,13 +29,6 @@ App::uses('CakeRule', 'Model/Validator'); */ class CakeField { -/** - * Holds the parent Validator instance - * - * @var ModelValidator - */ - protected $_validator = null; - /** * Holds the ValidationRule objects * @@ -43,6 +36,20 @@ class CakeField { */ protected $_rules = array(); +/** + * Set of methods available for validation + * + * @var array + **/ + protected $_methods = array(); + +/** + * I18n domain for validation messages. + * + * @var string + **/ + protected $_validationDomain = null; + /** * If the validation is stopped * @@ -67,13 +74,10 @@ class CakeField { /** * Constructor * - * @param ModelValidator $validator The parent ModelValidator * @param string $fieldName The fieldname - * @param + * @param array $ruleset */ - public function __construct(ModelValidator $validator, $fieldName, $ruleSet) { - $this->_validator = $validator; - $this->data = &$this->getValidator()->data; + public function __construct($fieldName, $ruleSet) { $this->field = $fieldName; if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) { @@ -81,41 +85,61 @@ class CakeField { } foreach ($ruleSet as $index => $validateProp) { - $this->_rules[$index] = new CakeRule($this, $validateProp, $index); + $this->_rules[$index] = new CakeRule($this->field, $validateProp, $index); } $this->ruleSet = $ruleSet; - unset($ruleSet, $validateProp); + } + +/** + * Sets the list of methods to use for validation + * + * @return void + **/ + public function setMethods(&$methods) { + $this->_methods =& $methods; + } + +/** + * Sets the I18n domain for validation messages. + * + * @param string $validationDomain The validation domain to be used. + * @return void + */ + public function setValidationDomain($validationDomain) { + $this->_validationDomain = $validationDomain; } /** * Validates a ModelField * - * @return mixed + * @return array list of validation errors for this field */ - public function validate() { + public function validate($data, $isUpdate = false) { + $errors = array(); foreach ($this->getRules() as $rule) { + $rule->isUpdate($isUpdate); if ($rule->skip()) { continue; } - $rule->isRequired(); - if (!$rule->checkRequired() && array_key_exists($this->field, $this->data)) { - if ($rule->checkEmpty()) { + $checkRequired = $rule->checkRequired($data); + if (!$checkRequired && array_key_exists($this->field, $data)) { + if ($rule->checkEmpty($data)) { break; } - $rule->dispatchValidation(); + $rule->dispatchValidation($data, $this->_methods); } - if ($rule->checkRequired() || !$rule->isValid()) { - $this->getValidator()->invalidate($this->field, $rule->getMessage()); + if ($checkRequired || !$rule->isValid($data)) { + $errors[] = $this->_processValidationResponse($rule); if ($rule->isLast()) { - return false; + break; } } } - return true; + return $errors; } /** @@ -168,21 +192,54 @@ class CakeField { } /** - * Gets the validator this field is atached to + * Fetches the correct error message for a failed validation * - * @return ModelValidator The parent ModelValidator instance + * @return string */ - public function getValidator() { - return $this->_validator; - } + protected function _processValidationResponse($rule) { + $message = $rule->getValidationResult(); + $name = $rule->getName(); + if (is_string($message)) { + return $message; + } + $message = $rule->message; -/** - * Magic isset - * - * @return true if the field exists in data, false otherwise - */ - public function __isset($fieldName) { - return array_key_exists($fieldName, $this->getValidator()->getData()); + if ($message !== null && !is_string($message)) { + $args = null; + if (is_array($message)) { + $result = $message[0]; + $args = array_slice($message, 1); + } else { + $result = $message; + } + if (is_array($rule->rule) && $args === null) { + $args = array_slice($rule->rule, 1); + } + if (!empty($args)) { + foreach ($args as $k => $arg) { + $args[$k] = __d($this->_validationDomain, $arg); + } + } + $message = __d($this->_validationDomain, $result, $args); + } elseif (is_string($name)) { + if (is_array($rule->rule)) { + $args = array_slice($rule->rule, 1); + if (!empty($args)) { + foreach ($args as $k => $arg) { + $args[$k] = __d($this->_validationDomain, $arg); + } + } + $message = __d($this->_validationDomain, $name, $args); + } else { + $message = __d($this->_validationDomain, $name); + } + //} elseif (!$rule->checkRequired() && is_numeric($name) && count($this->ruleSet) > 1) { + // $this->_errorMessage = $this->_index + 1; + } else { + $message = __d('cake_dev', 'This field cannot be left blank'); + } + + return $message; } } diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index c3cc7f738..744821cfd 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -37,13 +37,6 @@ class CakeRule { */ protected $_field = null; -/** - * Has the required check failed? - * - * @var boolean - */ - protected $_requiredFail = null; - /** * The 'valid' value * @@ -63,7 +56,7 @@ class CakeRule { * * @var boolean */ - protected $_modelExists = null; + protected $_recordExists = false; /** * The parsed rule @@ -79,13 +72,6 @@ class CakeRule { */ protected $_ruleParams = array(); -/** - * The errorMessage - * - * @var string - */ - protected $_errorMessage = null; - /** * Holds passed in options * @@ -93,13 +79,6 @@ class CakeRule { */ protected $_passedOptions = array(); -/** - * Flag indicating wether the allowEmpty check has failed - * - * @var boolean - */ - protected $_emptyFail = null; - /** * The 'rule' key * @@ -145,25 +124,13 @@ class CakeRule { /** * Constructor * - * @param CakeField $field * @param array $validator [optional] The validator properties * @param mixed $index [optional] */ - public function __construct(CakeField $field, $validator = array(), $index = null) { + public function __construct($field, $validator = array(), $index = null) { $this->_field = $field; $this->_index = $index; - unset($field, $index); - - $this->data = &$this->getField() - ->data; - - $this->_modelExists = $this->getField() - ->getValidator() - ->getModel() - ->exists(); - $this->_addValidatorProps($validator); - unset($validator); } /** @@ -188,9 +155,8 @@ class CakeRule { if (is_bool($this->required)) { return $this->required; } - if (in_array($this->required, array('create', 'update'), true)) { - if ($this->required === 'create' && !$this->_modelExists || $this->required === 'update' && $this->_modelExists) { + if ($this->required === 'create' && !$this->_recordExists || $this->required === 'update' && $this->_recordExists) { $this->required = true; } } @@ -201,37 +167,29 @@ class CakeRule { /** * Checks if the field failed the required validation * + * @param array $data data to check rule against * @return boolean */ - public function checkRequired() { - if ($this->_requiredFail !== null) { - return $this->_requiredFail; - } - $this->_requiredFail = ( - (!isset($this->data[$this->getField()->field]) && $this->required === true) || - ( - isset($this->data[$this->getField()->field]) && (empty($this->data[$this->getField()->field]) && - !is_numeric($this->data[$this->getField()->field])) && $this->allowEmpty === false - ) + public function checkRequired(&$data) { + $required = !isset($data[$this->_field]) && $this->required === true; + $required = $required || ( + isset($this->data[$this->_field]) && (empty($data[$this->_field]) && + !is_numeric($data[$this->_field])) && $this->allowEmpty === false ); - return $this->_requiredFail; + return $required; } /** * Checks if the allowEmpty key applies * + * @param array $data data to check rule against * @return boolean */ - public function checkEmpty() { - if ($this->_emptyFail !== null) { - return $this->_emptyFail; + public function checkEmpty(&$data) { + if (empty($data[$this->_field]) && $data[$this->_field] != '0' && $this->allowEmpty === true) { + return true; } - $this->_emptyFail = false; - - if (empty($this->data[$this->getField()->field]) && $this->data[$this->getField()->field] != '0' && $this->allowEmpty === true) { - $this->_emptyFail = true; - } - return $this->_emptyFail; + return false; } /** @@ -241,7 +199,7 @@ class CakeRule { */ public function skip() { if (!empty($this->on)) { - if ($this->on == 'create' && $this->_modelExists || $this->on == 'update' && !$this->_modelExists) { + if ($this->on == 'create' && $this->_recordExists || $this->on == 'update' && !$this->_recordExists) { return true; } } @@ -262,17 +220,8 @@ class CakeRule { * * @return string */ - public function getMessage() { - return $this->_processValidationResponse(); - } - -/** - * Gets the parent field - * - * @return CakeField - */ - public function getField() { - return $this->_field; + public function getValidationResult() { + return $this->_valid; } /** @@ -291,17 +240,27 @@ class CakeRule { ); } +/** + * Sets the recordExists configuration value for this rule, + * ir refers to wheter the model record it is validating exists + * exists in the collection or not (create or update operation) + * + * @return void + **/ + public function isUpdate($exists = false) { + $this->_recordExists = $exists; + } + /** * Dispatches the validation rule to the given validator method * * @return boolean True if the rule could be dispatched, false otherwise */ - public function dispatchValidation() { - $this->_parseRule(); + public function dispatchValidation(&$data, &$methods) { + $this->_parseRule($data); $validator = $this->getPropertiesArray(); - $methods = $this->getField()->getValidator()->getMethods(); - $Model = $this->getField()->getValidator()->getModel(); + $rule = strtolower($this->_rule); if (in_array(strtolower($this->_rule), $methods['model'])) { $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); @@ -319,58 +278,19 @@ class CakeRule { trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field->field), E_USER_WARNING); return false; } - unset($validator, $methods, $Model); return true; } -/** - * Fetches the correct error message for a failed validation - * - * @return string - */ - protected function _processValidationResponse() { - $validationDomain = $this->_field->getValidator()->validationDomain; - - if (is_string($this->_valid)) { - $this->_errorMessage = $this->_valid; - } elseif ($this->message !== null) { - $args = null; - if (is_array($this->message)) { - $this->_errorMessage = $this->message[0]; - $args = array_slice($this->message, 1); - } else { - $this->_errorMessage = $this->message; - } - if (is_array($this->rule) && $args === null) { - $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); - } - if (!empty($args)) { - foreach ($args as $k => $arg) { - $args[$k] = __d($validationDomain, $arg); - } - } - $this->_errorMessage = __d($validationDomain, $this->_errorMessage, $args); - } elseif (is_string($this->_index)) { - if (is_array($this->rule)) { - $args = array_slice($this->getField()->ruleSet[$this->_index]['rule'], 1); - if (!empty($args)) { - foreach ($args as $k => $arg) { - $args[$k] = __d($validationDomain, $arg); - } - } - $this->_errorMessage = __d($validationDomain, $this->_index, $args); - } else { - $this->_errorMessage = __d($validationDomain, $this->_index); - } - } elseif (!$this->checkRequired() && is_numeric($this->_index) && count($this->getField()->ruleSet) > 1) { - $this->_errorMessage = $this->_index + 1; - } else { - $this->_errorMessage = __d('cake_dev', 'This field cannot be left blank'); + public function getOptions($key) { + if (!isset($this->_passedOptions[$key])) { + return null; } - unset($validationDomain); + return $this->_passedOptions[$key]; + } - return $this->_errorMessage; + public function getName() { + return $this->_index; } /** @@ -386,13 +306,12 @@ class CakeRule { foreach ($validator as $key => $value) { if (isset($value) || !empty($value)) { if (in_array($key, array('rule', 'required', 'allowEmpty', 'on', 'message', 'last'))) { - $this->$key = $validator[$key]; + $this->{$key} = $validator[$key]; } else { $this->_passedOptions[$key] = $value; } } } - unset($validator); } /** @@ -400,14 +319,14 @@ class CakeRule { * * @return void */ - protected function _parseRule() { + protected function _parseRule(&$data) { if (is_array($this->rule)) { $this->_rule = $this->rule[0]; unset($this->rule[0]); - $this->_ruleParams = array_merge(array($this->data[$this->getField()->field]), array_values($this->rule)); + $this->_ruleParams = array_merge(array($data[$this->_field]), array_values($this->rule)); } else { $this->_rule = $this->rule; - $this->_ruleParams = array($this->data[$this->getField()->field]); + $this->_ruleParams = array($data[$this->_field]); } } From c31f87b7a130b5fa780493ad804186dc928b4e27 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 00:24:35 -0430 Subject: [PATCH 09/42] Refactored getMethods, got rid of validationErrors property in ModelValidator, made validateAssociated and many tests pass --- lib/Cake/Model/ModelValidator.php | 72 +++++++--------- lib/Cake/Model/Validator/CakeRule.php | 17 ++-- .../Test/Case/Model/ModelValidationTest.php | 85 +------------------ .../Case/Model/Validator/CakeFieldTest.php | 15 +--- 4 files changed, 43 insertions(+), 146 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index c90636fcf..c15dfc937 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -90,7 +90,6 @@ class ModelValidator { * @return boolean True if there are no errors */ public function validates($options = array()) { - $this->validationErrors = array(); $errors = $this->invalidFields($options); if (empty($errors) && $errors !== false) { $errors = $this->_validateWithModels($options); @@ -117,12 +116,12 @@ class ModelValidator { * depending on whether each record validated successfully. */ public function validateAssociated($data, $options = array()) { - $options = array_merge(array('atomic' => true, 'deep' => false), $options); $model = $this->getModel(); - $this->validationErrors = $model->validationErrors = $return = array(); - if (!($model->create($data) && $this->validates($options))) { - $this->validationErrors = array($model->alias => $this->validationErrors); + $options = array_merge(array('atomic' => true, 'deep' => false), $options); + $model->validationErrors = $validationErrors = $return = array(); + if (!($model->create($data) && $model->validates($options))) { + $validationErrors[$model->alias] = $model->validationErrors; $return[$model->alias] = false; } else { $return[$model->alias] = true; @@ -133,9 +132,9 @@ class ModelValidator { if (isset($associations[$association])) { if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { if ($options['deep']) { - $validates = $model->{$association}->validator()->validateAssociated($values, $options); + $validates = $model->{$association}->validateAssociated($values, $options); } else { - $validates = $model->{$association}->create($values) !== null && $model->{$association}->validator()->validates($options); + $validates = $model->{$association}->create($values) !== null && $model->{$association}->validates($options); } if (is_array($validates)) { if (in_array(false, $validates, true)) { @@ -146,23 +145,23 @@ class ModelValidator { } $return[$association] = $validates; } elseif ($associations[$association] === 'hasMany') { - $validates = $model->{$association}->validator()->validateMany($values, $options); + $validates = $model->{$association}->validateMany($values, $options); $return[$association] = $validates; } if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $this->validationErrors[$association] = $model->{$association}->validator()->validationErrors; + $validationErrors[$association] = $model->{$association}->validationErrors; } } } - if (isset($this->validationErrors[$model->alias])) { - $this->validationErrors = $this->validationErrors[$model->alias]; + $model->validationErrors = $validationErrors; + if (isset($validationErrors[$model->alias])) { + $model->validationErrors = $validationErrors[$model->alias]; } - $model->validationErrors = $this->validationErrors; if (!$options['atomic']) { return $return; } - if ($return[$model->alias] === false || !empty($this->validationErrors)) { + if ($return[$model->alias] === false || !empty($model->validationErrors)) { return false; } return true; @@ -185,29 +184,28 @@ class ModelValidator { * depending on whether each record validated successfully. */ public function validateMany($data, $options = array()) { - $options = array_merge(array('atomic' => true, 'deep' => false), $options); $model = $this->getModel(); - - $this->validationErrors = $validationErrors = $model->validationErrors = $return = array(); + $options = array_merge(array('atomic' => true, 'deep' => false), $options); + $model->validationErrors = $validationErrors = $return = array(); foreach ($data as $key => $record) { if ($options['deep']) { - $validates = $this->validateAssociated($record, $options); + $validates = $model->validateAssociated($record, $options); } else { - $validates = $model->create($record) && $this->validates($options); + $validates = $model->create($record) && $model->validates($options); } if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) { - $validationErrors[$key] = $this->validationErrors; + $validationErrors[$key] = $model->validationErrors; $validates = false; } else { $validates = true; } $return[$key] = $validates; } - $this->validationErrors = $model->validationErrors = $validationErrors; + $model->validationErrors = $validationErrors; if (!$options['atomic']) { return $return; } - if (empty($this->validationErrors)) { + if (empty($model->validationErrors)) { return true; } return false; @@ -228,7 +226,7 @@ class ModelValidator { $this->setOptions($options); if (!$this->_parseRules()) { - return $model->validationErrors = $this->validationErrors; + return $model->validationErrors; } $exists = $model->exists(); @@ -236,13 +234,14 @@ class ModelValidator { foreach ($this->_fields as $field) { $field->setMethods($methods); $field->setValidationDomain($model->validationDomain); - $errors = $field->validate($model->data, $exists); + $data = isset($model->data[$model->alias]) ? $model->data[$model->alias] : array(); + $errors = $field->validate($data, $exists); foreach ($errors as $error) { $this->invalidate($field->field, $error); } } - return $model->validationErrors = $this->validationErrors; + return $model->validationErrors; } /** @@ -258,35 +257,30 @@ class ModelValidator { if (!is_array($this->validationErrors)) { $this->validationErrors = array(); } - $this->validationErrors[$field][] = $this->getModel()->validationErrors[$field][] = $value; + $this->getModel()->validationErrors[$field][] = $value; } /** * Gets all possible custom methods from the Model, Behaviors and the Validator. - * If $type is null (default) gets all methods. If $type is one of 'model', 'behaviors' or 'validator', * gets the corresponding methods. * - * @param string $type [optional] The methods type to get. Defaults to null * @return array The requested methods */ - public function getMethods($type = null) { + public function getMethods() { if (!empty($this->_methods)) { - if ($type !== null && !empty($this->_methods[$type])) { - return $this->_methods[$type]; - } return $this->_methods; } - $this->_methods['model'] = array_map('strtolower', get_class_methods($this->_model)); - $this->_methods['behaviors'] = array_keys($this->_model->Behaviors->methods()); - $this->_methods['validator'] = get_class_methods($this); - - if ($type !== null && !empty($this->_methods[$type])) { - return $this->_methods[$type]; + $methods = array(); + foreach (get_class_methods($this->_model) as $method); { + $methods[strtolower($method)] = array($this->_model, $method); } - unset($type); - return $this->_methods; + foreach (array_keys($this->_model->Behaviors->methods()) as $mthod) { + $methods += array(strtolower($method) => array($this->_model, $method)); + } + + return $this->_methods = $methods; } /** diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 744821cfd..f64b34ba3 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -261,21 +261,16 @@ class CakeRule { $validator = $this->getPropertiesArray(); $rule = strtolower($this->_rule); - - if (in_array(strtolower($this->_rule), $methods['model'])) { + if (isset($methods[$rule])) { $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); - $this->_ruleParams[0] = array($this->getField()->field => $this->_ruleParams[0]); - $this->_valid = $Model->dispatchMethod($this->_rule, $this->_ruleParams); - } elseif (in_array($this->_rule, $methods['behaviors']) || in_array(strtolower($this->_rule), $methods['behaviors'])) { - $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); - $this->_ruleParams[0] = array($this->getField()->field => $this->_ruleParams[0]); - $this->_valid = $Model->Behaviors->dispatchMethod($Model, $this->_rule, $this->_ruleParams); - } elseif (method_exists('Validation', $this->_rule)) { + $this->_ruleParams[0] = array($this->_field => $this->_ruleParams[0]); + $this->_valid = call_user_func_array($methods[$rule], $this->_ruleParams); + } elseif (class_exists('Validation') && method_exists('Validation', $this->_rule)) { $this->_valid = call_user_func_array(array('Validation', $this->_rule), $this->_ruleParams); } elseif (!is_array($validator['rule'])) { - $this->_valid = preg_match($this->_rule, $this->data[$this->getField()->field]); + $this->_valid = preg_match($this->_rule, $data[$this->_field]); } elseif (Configure::read('debug') > 0) { - trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field->field), E_USER_WARNING); + trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field), E_USER_WARNING); return false; } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index f672e87c5..5563a1b1d 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1767,36 +1767,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($expected, $TestModel->validationErrors); } -/** - * testGetData method - * - * @return void - */ - public function testGetData() { - $this->loadFixtures('Article', 'Comment'); - $TestModel = new Article(); - $data = array( - 'Article' => array( - 'id' =>1, 'title' => 'first title' - ), - 'Comment' => array( - array('id' => 1, 'article_id' => 1, 'title' => 'first comment'), - array('id' => 2, 'article_id' => 1, 'title' => 'second comment') - ) - ); - $Validator = new ModelValidator($TestModel); - $TestModel->set($data); - $result = $Validator->getData(null, true); - $this->assertEquals($data, $result); - - $Validator->data = array(); - $result = $Validator->getData(); - $this->assertEquals($data['Article'], $result); - - $result = $Validator->getData('title'); - $this->assertEquals($data['Article']['title'], $result); - } - /** * testGetMethods method * @@ -1814,51 +1784,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($expected, $result['model']); } -/** - * testGetFields method - * - * @return void - */ - public function testGetFields() { - $this->loadFixtures('Article', 'Comment'); - $TestModel = new Article(); - $Validator = $TestModel->validator(); - - $result = $Validator->getFields(); - $this->assertSame(array(), $result); - - $Validator->setFields(); - $result = $Validator->getFields(); - $this->assertEquals(array('user_id', 'title','body'), array_keys($result)); - - $result = $Validator->getFields('title'); - $this->assertInstanceOf('CakeField', $result); - } - -/** - * testSetFields method - * - * @return void - */ - public function testSetFields() { - $this->loadFixtures('Article', 'Comment'); - $TestModel = new Article(); - $TestModel->whitelist = array('title', 'body'); - $Validator = $TestModel->validator(); - - $result = $Validator->setFields(); - $this->assertTrue($result); - - $result = $Validator->getFields(); - $this->assertEquals(array('title','body'), array_keys($result)); - - $result = $Validator->getFields('user_id'); - $this->assertFalse($result); - - $result = $Validator->getFields('body'); - $this->assertEquals(array('notEmpty'), $result->ruleSet); - } - /** * testSetOptions method * @@ -1912,15 +1837,11 @@ class ModelValidationTest extends BaseModelTest { $TestModel = new Article(); $Validator = $TestModel->validator(); - $result = $Validator->setValidationDomain(); - $this->assertEquals('default', $result->validationDomain); - - $TestModel->validationDomain = 'validation_messages'; - $result = $Validator->setValidationDomain(); - $this->assertEquals('validation_messages', $result->validationDomain); + $result = $Validator->setValidationDomain('default'); + $this->assertEquals('default', $TestModel->validationDomain); $result = $Validator->setValidationDomain('other'); - $this->assertEquals('other', $result->validationDomain); + $this->assertEquals('other', $TestModel->validationDomain); } /** diff --git a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php index 9774857e9..5d23d846a 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php @@ -32,24 +32,11 @@ class CakeFieldTest extends BaseModelTest { * @return void */ public function setUp() { + $this->skipIf(true); parent::setUp(); $this->Article = new Article(); $this->Article->set(array('title' => '', 'body' => 'no title')); $this->Validator = new ModelValidator($this->Article); - $this->Validator->getData(); - } - -/** - * testConstruct method - * - * @return void - */ - public function testConstruct() { - $Field = new CakeField($this->Validator, 'title', 'notEmpty'); - - $this->assertEquals(array('title' => '', 'body' => 'no title'), $Field->data); - $this->assertEquals('title', $Field->field); - $this->assertEquals(array('notEmpty'), $Field->ruleSet); } /** From 18c26119f3f4dbc8e814c4c44fc66b607e33630f Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 00:39:43 -0430 Subject: [PATCH 10/42] Removing redundant code, cleaning up some sentences --- lib/Cake/Model/ModelValidator.php | 2 +- lib/Cake/Model/Validator/CakeRule.php | 2 +- lib/Cake/Test/Case/Model/ModelValidationTest.php | 8 -------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index c15dfc937..37d66548e 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -272,7 +272,7 @@ class ModelValidator { } $methods = array(); - foreach (get_class_methods($this->_model) as $method); { + foreach (get_class_methods($this->_model) as $method) { $methods[strtolower($method)] = array($this->_model, $method); } diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index f64b34ba3..3af2591dd 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -267,7 +267,7 @@ class CakeRule { $this->_valid = call_user_func_array($methods[$rule], $this->_ruleParams); } elseif (class_exists('Validation') && method_exists('Validation', $this->_rule)) { $this->_valid = call_user_func_array(array('Validation', $this->_rule), $this->_ruleParams); - } elseif (!is_array($validator['rule'])) { + } elseif (is_string($validator['rule'])) { $this->_valid = preg_match($this->_rule, $data[$this->_field]); } elseif (Configure::read('debug') > 0) { trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field), E_USER_WARNING); diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 5563a1b1d..832ff1255 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -32,7 +32,6 @@ class ModelValidationTest extends BaseModelTest { */ public function testValidationParams() { $TestModel = new ValidationTest1(); - $Validator = new ModelValidator($TestModel); $TestModel->validate['title'] = array( 'rule' => 'customValidatorWithParams', 'required' => true @@ -56,8 +55,6 @@ class ModelValidationTest extends BaseModelTest { 'ignoreOnSame' => 'id' ); $this->assertEquals($expected, $TestModel->validatorParams); - $Validator->invalidFields(); - $this->assertEquals($expected, $TestModel->validatorParams); $TestModel->validate['title'] = array( 'rule' => 'customValidatorWithMessage', @@ -68,7 +65,6 @@ class ModelValidationTest extends BaseModelTest { ); $this->assertEquals($expected, $TestModel->invalidFields()); - $this->assertEquals($expected, $Validator->invalidFields()); $TestModel->validate['title'] = array( 'rule' => array('customValidatorWithSixParams', 'one', 'two', null, 'four'), @@ -95,8 +91,6 @@ class ModelValidationTest extends BaseModelTest { 'six' => 6 ); $this->assertEquals($expected, $TestModel->validatorParams); - $Validator->invalidFields(); - $this->assertEquals($expected, $TestModel->validatorParams); $TestModel->validate['title'] = array( 'rule' => array('customValidatorWithSixParams', 'one', array('two'), null, 'four', array('five' => 5)), @@ -123,8 +117,6 @@ class ModelValidationTest extends BaseModelTest { ) ); $this->assertEquals($expected, $TestModel->validatorParams); - $Validator->invalidFields(); - $this->assertEquals($expected, $TestModel->validatorParams); } /** From 30761f91536b2c0e7b41c6dc3c81ac0418a85354 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 00:46:56 -0430 Subject: [PATCH 11/42] Removed more redundant lines in ModelValidation tests --- .../Test/Case/Model/ModelValidationTest.php | 80 +------------------ 1 file changed, 3 insertions(+), 77 deletions(-) diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 832ff1255..97f52953a 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -126,7 +126,6 @@ class ModelValidationTest extends BaseModelTest { */ public function testInvalidFieldsWithFieldListParams() { $TestModel = new ValidationTest1(); - $Validator = new ModelValidator($TestModel); $TestModel->validate = $validate = array( 'title' => array( 'rule' => 'alphaNumeric', @@ -142,18 +141,14 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $Validator->invalidFields(array('fieldList' => array('title'))); - $this->assertEquals($expected, $Validator->validationErrors); - $TestModel->validationErrors = $Validator->validatiorErrors = array(); + $TestModel->validationErrors = array(); $TestModel->invalidFields(array('fieldList' => array('name'))); $expected = array( 'name' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $Validator->invalidFields(array('fieldList' => array('name'))); - $this->assertEquals($expected, $Validator->validationErrors); - $TestModel->validationErrors = $Validator->validatiorErrors = array(); + $TestModel->validationErrors = array(); $TestModel->invalidFields(array('fieldList' => array('name', 'title'))); $expected = array( @@ -161,16 +156,12 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('This field cannot be left blank') ); $this->assertEquals($expected, $TestModel->validationErrors); - $Validator->invalidFields(array('fieldList' => array('title', 'name'))); - $this->assertEquals($expected, $Validator->validationErrors); - $TestModel->validationErrors = $Validator->validatiorErrors = array(); + $TestModel->validationErrors = array(); $TestModel->whitelist = array('name'); $TestModel->invalidFields(); $expected = array('name' => array('This field cannot be left blank')); $this->assertEquals($expected, $TestModel->validationErrors); - $Validator->invalidFields(); - $this->assertEquals($expected, $Validator->validationErrors); $this->assertEquals($TestModel->validate, $validate); } @@ -182,7 +173,6 @@ class ModelValidationTest extends BaseModelTest { */ public function testInvalidFieldsWhitelist() { $TestModel = new ValidationTest1(); - $Validator = $TestModel->validator(); $TestModel->validate = array( 'title' => array( 'rule' => 'alphaNumeric', @@ -198,7 +188,6 @@ class ModelValidationTest extends BaseModelTest { $expected = array('name' => array('This field cannot be left blank')); $this->assertEquals($expected, $TestModel->validationErrors); - $this->assertEquals($expected, $Validator->validationErrors); } /** @@ -208,7 +197,6 @@ class ModelValidationTest extends BaseModelTest { */ public function testValidates() { $TestModel = new TestValidate(); - $Validator = new ModelValidator($TestModel); $TestModel->validate = array( 'user_id' => 'numeric', @@ -225,8 +213,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -235,8 +221,6 @@ class ModelValidationTest extends BaseModelTest { )); $result = $TestModel->create($data) && $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -247,8 +231,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -259,8 +241,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate['modified'] = array('allowEmpty' => true, 'rule' => 'date'); @@ -274,8 +254,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -287,8 +265,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -300,8 +276,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -313,8 +287,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -326,8 +298,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $TestModel->validate['modified'] = array('allowEmpty' => false, 'rule' => 'date'); @@ -336,30 +306,22 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array('modified' => false)); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array('modified' => '')); $result = $TestModel->create($data); $this->assertEquals($data, $result); - $result = $TestModel->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'modified' => '2007-05-01' )); $result = $TestModel->create($data); $this->assertEquals($data, $result); - $result = $TestModel->validates(); - $this->assertTrue($result); $TestModel->validate['slug'] = array('allowEmpty' => false, 'rule' => array('maxLength', 45)); @@ -373,8 +335,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -388,8 +348,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertTrue($result); $result = $Validator->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -401,8 +359,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $TestModel->validate = array( 'number' => array( @@ -423,8 +379,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -434,8 +388,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -445,8 +397,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -456,8 +406,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate = array( 'number' => array( @@ -478,8 +426,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'title', @@ -489,8 +435,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $TestModel->validate = array( 'title' => array( @@ -503,24 +447,18 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array('title' => 'new title')); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array('title' => 'title-new')); $result = $TestModel->create($data); $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate = array('title' => array( 'allowEmpty' => true, @@ -531,8 +469,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -545,8 +481,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -557,8 +491,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertFalse($result); - $result = $Validator->validates(); - $this->assertFalse($result); $data = array('TestValidate' => array( 'title' => 'My Article With a Different Title' @@ -567,8 +499,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $TestModel->validate = array( 'title' => array( @@ -589,8 +519,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($expected, $result); $result = $Validator->validates(); $this->assertFalse($result); - $result = $Validator->validationErrors; - $this->assertEquals($expected, $result); $TestModel->validate = array( 'title' => array( @@ -640,7 +568,6 @@ class ModelValidationTest extends BaseModelTest { $Something = new Something(); $JoinThing = $Something->JoinThing; - $Validator = $Something->JoinThing->validator(); $JoinThing->validate = array('doomed' => array('rule' => 'notEmpty')); @@ -650,7 +577,6 @@ class ModelValidationTest extends BaseModelTest { $result = $Something->save($data); $this->assertFalse($result, 'Save occurred even when with models failed. %s'); $this->assertEquals($expectedError, $JoinThing->validationErrors); - $this->assertEquals($expectedError, $Validator->validationErrors); $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); $this->assertSame($count, 0); From 8700f3c7a229b7836a18e2ddc98a97ad06b674ef Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 01:06:24 -0430 Subject: [PATCH 12/42] Fixed some validation tests --- lib/Cake/Model/ModelValidator.php | 3 +-- lib/Cake/Model/Validator/CakeRule.php | 11 ++++++----- lib/Cake/Test/Case/Model/ModelValidationTest.php | 6 ------ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 37d66548e..86f0f1627 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -311,13 +311,12 @@ class ModelValidator { return false; } - if (!empty($this->_validate) && $this->_validate === $this->_model->validate) { + if ($this->_validate === $this->_model->validate) { return true; } $this->_validate = $this->_model->validate; $this->_processWhitelist(); - $this->_fields = array(); $methods = $this->getMethods(); foreach ($this->_validate as $fieldName => $ruleSet) { diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 3af2591dd..97eff77f0 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -171,12 +171,13 @@ class CakeRule { * @return boolean */ public function checkRequired(&$data) { - $required = !isset($data[$this->_field]) && $this->required === true; - $required = $required || ( - isset($this->data[$this->_field]) && (empty($data[$this->_field]) && - !is_numeric($data[$this->_field])) && $this->allowEmpty === false + return ( + (!isset($data[$this->_field]) && $this->required === true) || + ( + isset($data[$this->_field]) && (empty($data[$this->_field]) && + !is_numeric($data[$this->_field])) && $this->allowEmpty === false + ) ); - return $required; } /** diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 97f52953a..c60492095 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -346,8 +346,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($data, $result); $result = $TestModel->validates(); $this->assertTrue($result); - $result = $Validator->validates(); - $this->assertTrue($result); $data = array('TestValidate' => array( 'user_id' => '1', @@ -517,8 +515,6 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('tooShort') ); $this->assertEquals($expected, $result); - $result = $Validator->validates(); - $this->assertFalse($result); $TestModel->validate = array( 'title' => array( @@ -540,8 +536,6 @@ class ModelValidationTest extends BaseModelTest { 'title' => array('tooShort', 'onlyLetters') ); $this->assertEquals($expected, $result); - $result = $Validator->validates(); - $this->assertFalse($result); $result = $TestModel->validationErrors; $this->assertEquals($expected, $result); } From 66a568be8915dbf081385580d4ae17629514d700 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 01:09:36 -0430 Subject: [PATCH 13/42] More tests passing now --- lib/Cake/Test/Case/Model/ModelValidationTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index c60492095..58eead103 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -602,6 +602,7 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testValidatesWithModelsAndSaveAll() { + $this->loadFixtures('Something', 'SomethingElse', 'JoinThing'); $data = array( 'Something' => array( 'id' => 5, @@ -627,13 +628,11 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($expectedError, $JoinThing->validationErrors); $result = $Something->validator()->validateAssociated($data); $this->assertFalse($result); - $this->assertEquals($expectedError, $JoinThing->validator()->validationErrors); $Something->create(); $result = $Something->saveAll($data, array('validate' => 'first')); $this->assertFalse($result); $this->assertEquals($expectedError, $JoinThing->validationErrors); - $this->assertEquals($expectedError, $JoinThing->validator()->validationErrors); $count = $Something->find('count', array('conditions' => array('Something.id' => $data['Something']['id']))); $this->assertSame($count, 0); From deedd7a3ce55dda1db05f7eb2ba1858a1e6a3635 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 02:40:08 -0430 Subject: [PATCH 14/42] Even more tests passing now, almost there --- lib/Cake/Model/Validator/CakeField.php | 3 +- lib/Cake/Model/Validator/CakeRule.php | 9 ++++-- .../Test/Case/Model/ModelValidationTest.php | 29 ------------------- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php index 311c2b386..afe60128a 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeField.php @@ -204,7 +204,7 @@ class CakeField { } $message = $rule->message; - if ($message !== null && !is_string($message)) { + if ($message !== null) { $args = null; if (is_array($message)) { $result = $message[0]; @@ -220,6 +220,7 @@ class CakeField { $args[$k] = __d($this->_validationDomain, $arg); } } + $message = __d($this->_validationDomain, $result, $args); } elseif (is_string($name)) { if (is_array($rule->rule)) { diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 97eff77f0..9ea0d6304 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -231,8 +231,12 @@ class CakeRule { * @return array */ public function getPropertiesArray() { + $rule = $this->rule; + if (!is_string($rule)) { + unset($rule[0]); + } return array( - 'rule' => $this->rule, + 'rule' => $rule, 'required' => $this->required, 'allowEmpty' => $this->allowEmpty, 'on' => $this->on, @@ -318,8 +322,7 @@ class CakeRule { protected function _parseRule(&$data) { if (is_array($this->rule)) { $this->_rule = $this->rule[0]; - unset($this->rule[0]); - $this->_ruleParams = array_merge(array($data[$this->_field]), array_values($this->rule)); + $this->_ruleParams = array_merge(array($data[$this->_field]), array_values(array_slice($this->rule, 1))); } else { $this->_rule = $this->rule; $this->_ruleParams = array($data[$this->_field]); diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 58eead103..13620ae31 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -671,7 +671,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertTrue($result); $result = $Author->validateAssociated($data); $this->assertTrue($result); - $result = $Author->validator()->validateAssociated($data); $this->assertTrue($result); $Author->create(); @@ -727,8 +726,6 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->invalidFields(array('fieldList' => array('title'))); $this->assertEquals(array(), $TestModel->validationErrors); - $TestModel->validator()->invalidFields(array('fieldList' => array('title'))); - $this->assertEquals(array(), $TestModel->validator()->validationErrors); } /** @@ -762,10 +759,6 @@ class ModelValidationTest extends BaseModelTest { $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->validator()->validationErrors = array(); - $TestModel->validator()->invalidFields(); - $this->assertEquals($expected, $TestModel->validator()->validationErrors); - $TestModel->create(array('title' => 'foo')); $expected = array( 'title' => array( @@ -775,10 +768,6 @@ class ModelValidationTest extends BaseModelTest { ); $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - - $TestModel->validator()->validationErrors = array(); - $TestModel->validator()->invalidFields(); - $this->assertEquals($expected, $TestModel->validator()->validationErrors); } /** @@ -814,10 +803,6 @@ class ModelValidationTest extends BaseModelTest { $TestModel->invalidFields(); $this->assertEquals($expected, $TestModel->validationErrors); - $TestModel->validator()->validationErrors = array(); - $TestModel->validator()->invalidFields(); - $this->assertEquals($expected, $TestModel->validator()->validationErrors); - $TestModel->validationDomain = 'default'; Configure::write('Config.language', $lang); App::build(); @@ -851,13 +836,11 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->validator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -871,13 +854,11 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->validator()->validates()); } /** @@ -908,13 +889,11 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->validator()->validates()); unset($data['Article']['id']); $Article->validate = array( @@ -928,13 +907,11 @@ class ModelValidationTest extends BaseModelTest { $Article->create($data); $this->assertTrue($Article->validates()); - $this->assertTrue($Article->validator()->validates()); $Article->save(null, array('validate' => false)); $data['Article']['id'] = $Article->id; $Article->set($data); $this->assertFalse($Article->validates()); - $this->assertFalse($Article->validator()->validates()); } /** @@ -1086,8 +1063,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertTrue($result); $result = $TestModel->validateAssociated($data, array('deep' => true)); $this->assertTrue($result); - $result = $TestModel->validator()->validateAssociated($data, array('deep' => true)); - $this->assertTrue($result); $data = array( 'Article' => array('id' => 2), @@ -1100,8 +1075,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertFalse($result); $result = $TestModel->validateAssociated($data, array('deep' => true)); $this->assertFalse($result); - $result = $TestModel->validator()->validateAssociated($data, array('deep' => true)); - $this->assertFalse($result); $data = array( 'Article' => array('id' => 2), @@ -1121,8 +1094,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertSame($expected, $result); $result = $TestModel->validateAssociated($data, array('atomic' => false, 'deep' => true)); $this->assertSame($expected, $result); - $result = $TestModel->validator()->validateAssociated($data, array('atomic' => false, 'deep' => true)); - $this->assertSame($expected, $result); $data = array( 'Article' => array('id' => 2), From 94040859b24dc81867bbb2f3721a21170900a2c8 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 02:57:10 -0430 Subject: [PATCH 15/42] Correctly calling method to not depend on functon call order --- lib/Cake/Model/Validator/CakeRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 9ea0d6304..c40253b30 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -172,7 +172,7 @@ class CakeRule { */ public function checkRequired(&$data) { return ( - (!isset($data[$this->_field]) && $this->required === true) || + (!isset($data[$this->_field]) && $this->isRequired() === true) || ( isset($data[$this->_field]) && (empty($data[$this->_field]) && !is_numeric($data[$this->_field])) && $this->allowEmpty === false From d348bf68074fb9100dd3a26f260e73985bf1e28b Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 17:18:33 -0430 Subject: [PATCH 16/42] Refactoring again CakeRule, making all tests pass --- lib/Cake/Model/ModelValidator.php | 7 +- lib/Cake/Model/Validator/CakeField.php | 13 ++-- lib/Cake/Model/Validator/CakeRule.php | 38 ++++------- .../Test/Case/Model/ModelValidationTest.php | 3 +- .../Case/Model/Validator/CakeFieldTest.php | 68 ++++++++----------- .../Case/Model/Validator/CakeRuleTest.php | 10 +-- 6 files changed, 58 insertions(+), 81 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 86f0f1627..f21bd28cb 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -376,8 +376,11 @@ class ModelValidator { * @param string $validationDomain [optional] The validation domain to be used. * @return ModelValidator */ - public function setValidationDomain($validationDomain) { - $model->validationDomain = $validationDomain; + public function setValidationDomain($validationDomain = null) { + if (empty($validationDomain)) { + $validationDomain = 'default'; + } + $this->getModel()->validationDomain = $validationDomain; return $this; } diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php index afe60128a..f44618831 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeField.php @@ -85,7 +85,7 @@ class CakeField { } foreach ($ruleSet as $index => $validateProp) { - $this->_rules[$index] = new CakeRule($this->field, $validateProp, $index); + $this->_rules[$index] = new CakeRule($index, $validateProp); } $this->ruleSet = $ruleSet; } @@ -122,17 +122,16 @@ class CakeField { continue; } - $checkRequired = $rule->checkRequired($data); + $checkRequired = $rule->checkRequired($this->field, $data); if (!$checkRequired && array_key_exists($this->field, $data)) { - if ($rule->checkEmpty($data)) { + if ($rule->checkEmpty($this->field, $data)) { break; } - $rule->dispatchValidation($data, $this->_methods); + $rule->dispatchValidation($this->field, $data, $this->_methods); } - if ($checkRequired || !$rule->isValid($data)) { + if ($checkRequired || !$rule->isValid()) { $errors[] = $this->_processValidationResponse($rule); - if ($rule->isLast()) { break; } @@ -234,8 +233,6 @@ class CakeField { } else { $message = __d($this->_validationDomain, $name); } - //} elseif (!$rule->checkRequired() && is_numeric($name) && count($this->ruleSet) > 1) { - // $this->_errorMessage = $this->_index + 1; } else { $message = __d('cake_dev', 'This field cannot be left blank'); } diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index c40253b30..5b8d3ea23 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -30,13 +30,6 @@ App::uses('Validation', 'Utility'); */ class CakeRule { -/** - * Holds a reference to the parent field - * - * @var CakeField - */ - protected $_field = null; - /** * The 'valid' value * @@ -127,8 +120,7 @@ class CakeRule { * @param array $validator [optional] The validator properties * @param mixed $index [optional] */ - public function __construct($field, $validator = array(), $index = null) { - $this->_field = $field; + public function __construct($index = null, $validator = array()) { $this->_index = $index; $this->_addValidatorProps($validator); } @@ -170,12 +162,12 @@ class CakeRule { * @param array $data data to check rule against * @return boolean */ - public function checkRequired(&$data) { + public function checkRequired($field, &$data) { return ( - (!isset($data[$this->_field]) && $this->isRequired() === true) || + (!isset($data[$field]) && $this->isRequired() === true) || ( - isset($data[$this->_field]) && (empty($data[$this->_field]) && - !is_numeric($data[$this->_field])) && $this->allowEmpty === false + isset($data[$field]) && (empty($data[$field]) && + !is_numeric($data[$field])) && $this->allowEmpty === false ) ); } @@ -186,8 +178,8 @@ class CakeRule { * @param array $data data to check rule against * @return boolean */ - public function checkEmpty(&$data) { - if (empty($data[$this->_field]) && $data[$this->_field] != '0' && $this->allowEmpty === true) { + public function checkEmpty($field, &$data) { + if (empty($data[$field]) && $data[$field] != '0' && $this->allowEmpty === true) { return true; } return false; @@ -261,21 +253,21 @@ class CakeRule { * * @return boolean True if the rule could be dispatched, false otherwise */ - public function dispatchValidation(&$data, &$methods) { - $this->_parseRule($data); + public function dispatchValidation($field, &$data, &$methods) { + $this->_parseRule($field, $data); $validator = $this->getPropertiesArray(); $rule = strtolower($this->_rule); if (isset($methods[$rule])) { $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); - $this->_ruleParams[0] = array($this->_field => $this->_ruleParams[0]); + $this->_ruleParams[0] = array($field => $this->_ruleParams[0]); $this->_valid = call_user_func_array($methods[$rule], $this->_ruleParams); } elseif (class_exists('Validation') && method_exists('Validation', $this->_rule)) { $this->_valid = call_user_func_array(array('Validation', $this->_rule), $this->_ruleParams); } elseif (is_string($validator['rule'])) { - $this->_valid = preg_match($this->_rule, $data[$this->_field]); + $this->_valid = preg_match($this->_rule, $data[$field]); } elseif (Configure::read('debug') > 0) { - trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $this->_field), E_USER_WARNING); + trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $field), E_USER_WARNING); return false; } @@ -319,13 +311,13 @@ class CakeRule { * * @return void */ - protected function _parseRule(&$data) { + protected function _parseRule($field, &$data) { if (is_array($this->rule)) { $this->_rule = $this->rule[0]; - $this->_ruleParams = array_merge(array($data[$this->_field]), array_values(array_slice($this->rule, 1))); + $this->_ruleParams = array_merge(array($data[$field]), array_values(array_slice($this->rule, 1))); } else { $this->_rule = $this->rule; - $this->_ruleParams = array($data[$this->_field]); + $this->_ruleParams = array($data[$field]); } } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 13620ae31..3aed0c348 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1660,10 +1660,9 @@ class ModelValidationTest extends BaseModelTest { $Validator = $TestModel->validator(); $result = $Validator->getMethods(); - $this->assertEquals(array('model', 'behaviors', 'validator'), array_keys($result)); $expected = array_map('strtolower', get_class_methods('Article')); - $this->assertEquals($expected, $result['model']); + $this->assertEquals($expected, array_keys($result)); } /** diff --git a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php index 5d23d846a..fdf064dbb 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php @@ -17,14 +17,14 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -require_once dirname(dirname(__FILE__)) . DS . 'ModelTestBase.php'; +App::uses('CakeField', 'Model/Validator'); /** * CakeFieldTest * * @package Cake.Test.Case.Model.Validator */ -class CakeFieldTest extends BaseModelTest { +class CakeFieldTest extends CakeTestModel { /** * setUp method @@ -32,11 +32,7 @@ class CakeFieldTest extends BaseModelTest { * @return void */ public function setUp() { - $this->skipIf(true); parent::setUp(); - $this->Article = new Article(); - $this->Article->set(array('title' => '', 'body' => 'no title')); - $this->Validator = new ModelValidator($this->Article); } /** @@ -45,20 +41,26 @@ class CakeFieldTest extends BaseModelTest { * @return void */ public function testValidate() { - $Field = new CakeField($this->Validator, 'title', 'notEmpty'); + $Field = new CakeField('title', 'notEmpty'); + $data = array( + 'title' => '', + 'body' => 'a body' + ); - $result = $Field->validate(); - $this->assertFalse($result); + $result = $Field->validate($data); + $expected = array('This field cannot be left blank'); + $this->assertEquals($expected, $result); - $Field = new CakeField($this->Validator, 'body', 'notEmpty'); + $Field = new CakeField('body', 'notEmpty'); - $result = $Field->validate(); - $this->assertTrue($result); + $result = $Field->validate($data); + $this->assertEmpty($result); - $Field = new CakeField($this->Validator, 'nothere', array('notEmpty' => array('rule' => 'notEmpty', 'required' => true))); + $Field = new CakeField('nothere', array('notEmpty' => array('rule' => 'notEmpty', 'required' => true))); - $result = $Field->validate(); - $this->assertFalse($result); + $result = $Field->validate($data); + $expected = array('notEmpty'); + $this->assertEquals($expected, $result); } /** @@ -68,7 +70,11 @@ class CakeFieldTest extends BaseModelTest { */ public function testGetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField($this->Validator, 'title', $rules); + $Field = new CakeField('title', $rules); + $data = array( + 'title' => '', + 'body' => 'a body' + ); $result = $Field->getRule('notEmpty'); $this->assertInstanceOf('CakeRule', $result); @@ -78,7 +84,6 @@ class CakeFieldTest extends BaseModelTest { $this->assertEquals(null, $result->on); $this->assertEquals(true, $result->last); $this->assertEquals('Can not be empty', $result->message); - $this->assertEquals(array('title' => '', 'body' => 'no title'), $result->data); } /** @@ -88,7 +93,7 @@ class CakeFieldTest extends BaseModelTest { */ public function testGetRules() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField($this->Validator, 'title', $rules); + $Field = new CakeField('title', $rules); $result = $Field->getRules(); $this->assertEquals(array('notEmpty'), array_keys($result)); @@ -102,19 +107,19 @@ class CakeFieldTest extends BaseModelTest { */ public function testSetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField($this->Validator, 'title', $rules); - $Rule = new CakeRule($Field, $rules['notEmpty'], 'notEmpty'); + $Field = new CakeField('title', $rules); + $Rule = new CakeRule('notEmpty', $rules['notEmpty']); $this->assertEquals($Rule, $Field->getRule('notEmpty')); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $Rule = new CakeRule($Field, $rules['validEmail'], 'validEmail'); + $Rule = new CakeRule('validEmail', $rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Other message')); - $Rule = new CakeRule($Field, $rules['validEmail'], 'validEmail'); + $Rule = new CakeRule('validEmail', $rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); @@ -126,7 +131,6 @@ class CakeFieldTest extends BaseModelTest { $this->assertEquals(null, $result->on); $this->assertEquals(true, $result->last); $this->assertEquals('Other message', $result->message); - $this->assertEquals(array('title' => '', 'body' => 'no title'), $result->data); } /** @@ -136,11 +140,11 @@ class CakeFieldTest extends BaseModelTest { */ public function testSetRules() { $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField($this->Validator, 'title', $rule); - $RuleEmpty = new CakeRule($Field, $rule['notEmpty'], 'notEmpty'); + $Field = new CakeField('title', $rule); + $RuleEmpty = new CakeRule('title', $rule['notEmpty'], 'notEmpty'); $rule = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $RuleEmail = new CakeRule($Field, $rule['validEmail'], 'validEmail'); + $RuleEmail = new CakeRule('email', $rule['validEmail'], 'validEmail'); $rules = array('validEmail' => $RuleEmail); $Field->setRules($rules, false); @@ -153,16 +157,4 @@ class CakeFieldTest extends BaseModelTest { $this->assertEquals(array('validEmail', 'notEmpty'), array_keys($result)); } -/** - * testGetValidator method - * - * @return void - */ - public function testGetValidator() { - $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField($this->Validator, 'title', $rule); - $result = $Field->getValidator(); - $this->assertInstanceOf('ModelValidator', $result); - } - } diff --git a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php index e95013701..d23afa155 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php @@ -17,14 +17,14 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -require_once dirname(dirname(__FILE__)) . DS . 'ModelTestBase.php'; +App::uses('CakeRule', 'Model/Validator'); /** * CakeRuleTest * * @package Cake.Test.Case.Model.Validator */ -class CakeRuleTest extends BaseModelTest { +class CakeRuleTest extends CakeTestModel { /** * setUp method @@ -33,12 +33,6 @@ class CakeRuleTest extends BaseModelTest { */ public function setUp() { parent::setUp(); - $Article = new Article(); - $Article->set(array('title' => '', 'body' => 'no title')); - $this->Validator = new ModelValidator($Article); - $this->Validator->getData(); - $rule = array('notEmpty' => array('rule' => 'notEmpty', 'required' => true, 'last' => false)); - $this->Field = new CakeField($this->Validator, 'body', $rule); } /** From 72e8992c25119f44a611dc1d16be6d0875809695 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 19:56:24 -0430 Subject: [PATCH 17/42] Improving test coverage for CakeRule --- lib/Cake/Model/Validator/CakeField.php | 2 +- lib/Cake/Model/Validator/CakeRule.php | 20 ++-- .../Case/Model/Validator/CakeRuleTest.php | 93 ++++++++++++++++++- 3 files changed, 106 insertions(+), 9 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeField.php index f44618831..901007a68 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeField.php @@ -127,7 +127,7 @@ class CakeField { if ($rule->checkEmpty($this->field, $data)) { break; } - $rule->dispatchValidation($this->field, $data, $this->_methods); + $rule->process($this->field, $data, $this->_methods); } if ($checkRequired || !$rule->isValid()) { diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 5b8d3ea23..5dfbbad07 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -148,8 +148,10 @@ class CakeRule { return $this->required; } if (in_array($this->required, array('create', 'update'), true)) { - if ($this->required === 'create' && !$this->_recordExists || $this->required === 'update' && $this->_recordExists) { + if ($this->required === 'create' && !$this->isUpdate() || $this->required === 'update' && $this->isUpdate()) { $this->required = true; + } else { + $this->required = false; } } @@ -192,7 +194,7 @@ class CakeRule { */ public function skip() { if (!empty($this->on)) { - if ($this->on == 'create' && $this->_recordExists || $this->on == 'update' && !$this->_recordExists) { + if ($this->on == 'create' && $this->isUpdate() || $this->on == 'update' && !$this->isUpdate()) { return true; } } @@ -242,10 +244,16 @@ class CakeRule { * ir refers to wheter the model record it is validating exists * exists in the collection or not (create or update operation) * - * @return void + * If called with no parameters it will return whether this rule + * is configured for update operations or not. + * + * @return boolean **/ - public function isUpdate($exists = false) { - $this->_recordExists = $exists; + public function isUpdate($exists = null) { + if ($exists === null) { + return $this->_recordExists; + } + return $this->_recordExists = $exists; } /** @@ -253,7 +261,7 @@ class CakeRule { * * @return boolean True if the rule could be dispatched, false otherwise */ - public function dispatchValidation($field, &$data, &$methods) { + public function process($field, &$data, &$methods) { $this->_parseRule($field, $data); $validator = $this->getPropertiesArray(); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php index d23afa155..0e79d4832 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php @@ -24,7 +24,7 @@ App::uses('CakeRule', 'Model/Validator'); * * @package Cake.Test.Case.Model.Validator */ -class CakeRuleTest extends CakeTestModel { +class CakeRuleTest extends CakeTestCase { /** * setUp method @@ -36,11 +36,100 @@ class CakeRuleTest extends CakeTestModel { } /** - * testIsValid method + * Auxiliary method to test custom validators + * + * @return boolean + **/ + public function myTestRule() { + return false; + } + +/** + * Auxiliary method to test custom validators + * + * @return boolean + **/ + public function myTestRule2() { + return true; + } + +/** + * Auxiliary method to test custom validators + * + * @return string + **/ + public function myTestRule3() { + return 'string'; + } + +/** + * Test isValid method * * @return void */ public function testIsValid() { + $def = array('rule' => 'notEmpty', 'message' => 'Can not be empty'); + $data = array( + 'fieldName' => '' + ); + $methods = array(); + $Rule = new CakeRule('notEmpty', $def); + $Rule->process('fieldName', $data, $methods); + $this->assertFalse($Rule->isValid()); + + $data = array('fieldName' => 'not empty'); + $Rule->process('fieldName', $data, $methods); + $this->assertTrue($Rule->isValid()); + } +/** + * tests that passing custom validation methods work + * + * @return void + */ + public function testCustomMethods() { + $def = array('rule' => 'myTestRule'); + $data = array( + 'fieldName' => 'some data' + ); + $methods = array('mytestrule' => array($this, 'myTestRule')); + + $Rule = new CakeRule('custom', $def); + $Rule->process('fieldName', $data, $methods); + $this->assertFalse($Rule->isValid()); + + $methods = array('mytestrule' => array($this, 'myTestRule2')); + $Rule->process('fieldName', $data, $methods); + $this->assertTrue($Rule->isValid()); + + $methods = array('mytestrule' => array($this, 'myTestRule3')); + $Rule->process('fieldName', $data, $methods); + $this->assertFalse($Rule->isValid()); + } + +/** + * Test isRequired method + * + * @return void + */ + public function testIsRequired() { + $def = array('rule' => 'notEmpty', 'required' => true); + $Rule = new CakeRule('required', $def); + $this->assertTrue($Rule->isRequired()); + + $def = array('rule' => 'notEmpty', 'required' => false); + $Rule = new CakeRule('required', $def); + $this->assertFalse($Rule->isRequired()); + + $def = array('rule' => 'notEmpty', 'required' => 'create'); + $Rule = new CakeRule('required', $def); + $this->assertTrue($Rule->isRequired()); + + $def = array('rule' => 'notEmpty', 'required' => 'update'); + $Rule = new CakeRule('required', $def); + $this->assertFalse($Rule->isRequired()); + + $Rule->isUpdate(true); + $this->assertTrue($Rule->isRequired()); } } From 05abff6ecd2f318fabb364160b320b53555db916 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 29 Apr 2012 19:58:22 -0430 Subject: [PATCH 18/42] Fixing faling test case --- lib/Cake/Model/Validator/CakeRule.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 5dfbbad07..f5b33253d 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -144,14 +144,11 @@ class CakeRule { * @return boolean */ public function isRequired() { - if (is_bool($this->required)) { - return $this->required; - } if (in_array($this->required, array('create', 'update'), true)) { if ($this->required === 'create' && !$this->isUpdate() || $this->required === 'update' && $this->isUpdate()) { - $this->required = true; + return true; } else { - $this->required = false; + return false; } } From 1e28e2f89578a680a6e3b19fb6cd09d7f5026f6d Mon Sep 17 00:00:00 2001 From: Ceeram Date: Tue, 1 May 2012 09:59:24 +0200 Subject: [PATCH 19/42] fixing typo that causes failing BehaviorCollection test --- lib/Cake/Model/ModelValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index f21bd28cb..91c2c221d 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -276,7 +276,7 @@ class ModelValidator { $methods[strtolower($method)] = array($this->_model, $method); } - foreach (array_keys($this->_model->Behaviors->methods()) as $mthod) { + foreach (array_keys($this->_model->Behaviors->methods()) as $method) { $methods += array(strtolower($method) => array($this->_model, $method)); } From 843d95bb77180907830b9b706180a5dc2dc867b9 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 13:34:14 -0430 Subject: [PATCH 20/42] Renaming some methods ands removing others --- lib/Cake/Model/Model.php | 2 +- lib/Cake/Model/ModelValidator.php | 92 +++++-------------- .../Test/Case/Model/ModelValidationTest.php | 43 --------- 3 files changed, 22 insertions(+), 115 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 2cbabaf18..97b364543 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -2989,7 +2989,7 @@ class Model extends Object implements CakeEventListener { * @see Model::validates() */ public function invalidFields($options = array()) { - return $this->validator()->invalidFields($options); + return $this->validator()->errors($options); } /** diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index f21bd28cb..d425c7d57 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -36,13 +36,6 @@ class ModelValidator { */ public $validationErrors = array(); -/** - * Holds the options - * - * @var array - */ - public $options = array(); - /** * Holds the CakeField objects array * @@ -90,7 +83,7 @@ class ModelValidator { * @return boolean True if there are no errors */ public function validates($options = array()) { - $errors = $this->invalidFields($options); + $errors = $this->errors($options); if (empty($errors) && $errors !== false) { $errors = $this->_validateWithModels($options); } @@ -218,20 +211,22 @@ class ModelValidator { * @return array Array of invalid fields * @see Model::validates() */ - public function invalidFields($options = array()) { - if (!$this->propagateBeforeValidate($options)) { + public function errors($options = array()) { + if (!$this->_triggerBeforeValidate($options)) { return false; } $model = $this->getModel(); - $this->setOptions($options); if (!$this->_parseRules()) { return $model->validationErrors; } + $fieldList = isset($options['fieldList']) ? $options['fieldList'] : array(); $exists = $model->exists(); $methods = $this->getMethods(); - foreach ($this->_fields as $field) { + $fields = $this->_validationList($fieldList); + + foreach ($fields as $field) { $field->setMethods($methods); $field->setValidationDomain($model->validationDomain); $data = isset($model->data[$model->alias]) ? $model->data[$model->alias] : array(); @@ -249,14 +244,11 @@ class ModelValidator { * rule (in case of multiple validation for field) that was broken. * * @param string $field The name of the field to invalidate - * @param mixed $value Name of validation rule that was not failed, or validation message to + * @param string $value Name of validation rule that failed, or validation message to * be returned. If no validation key is provided, defaults to true. * @return void */ public function invalidate($field, $value = true) { - if (!is_array($this->validationErrors)) { - $this->validationErrors = array(); - } $this->getModel()->validationErrors[$field][] = $value; } @@ -316,7 +308,6 @@ class ModelValidator { } $this->_validate = $this->_model->validate; - $this->_processWhitelist(); $this->_fields = array(); $methods = $this->getMethods(); foreach ($this->_validate as $fieldName => $ruleSet) { @@ -325,51 +316,6 @@ class ModelValidator { return true; } -/** - * Sets an options array. If $mergeVars is true, the options will be merged with the existing ones. - * Otherwise they will get replaced. The default is merging the vars. - * - * @param array $options [optional] The options to be set - * @param boolean $mergeVars [optional] If true, the options will be merged, otherwise they get replaced - * @return ModelValidator - */ - public function setOptions($options = array(), $mergeVars = false) { - if ($mergeVars === false) { - $this->options = $options; - } else { - $this->options = array_merge($this->options, $options); - } - return $this; - } - -/** - * Sets an option $name with $value. This method is chainable - * - * @param string $name The options name to be set - * @param mixed $value [optional] The value to be set. Defaults to null. - * @return ModelValidator - */ - public function setOption($name, $value = null) { - $this->options[$name] = $value; - return $this; - } - -/** - * Gets an options value by $name. If $name is not set or no option has been found, returns null. - * - * @param string $name The options name to look up - * @return mixed Either null or the option value - */ - public function getOptions($name = null) { - if ($name !== null) { - if (!isset($this->options[$name])) { - return null; - } - return $this->options[$name]; - } - return $this->options; - } - /** * Sets the I18n domain for validation messages. This method is chainable. * @@ -394,14 +340,14 @@ class ModelValidator { } /** - * Processes the Model's whitelist and adjusts the validate array accordingly + * Processes the Model's whitelist and returns the list of fields + * to be validated * - * @return void + * @return array List of validation rules to be applied */ - protected function _processWhitelist() { + protected function _validationList($fieldList = array()) { $model = $this->getModel(); $whitelist = $model->whitelist; - $fieldList = $this->getOptions('fieldList'); if (!empty($fieldList)) { if (!empty($fieldList[$model->alias]) && is_array($fieldList[$model->alias])) { @@ -412,16 +358,20 @@ class ModelValidator { } unset($fieldList); + $validateList = array(); if (!empty($whitelist)) { $this->validationErrors = array(); - $validate = array(); + foreach ((array)$whitelist as $f) { - if (!empty($this->_validate[$f])) { - $validate[$f] = $this->_validate[$f]; + if (!empty($this->_fields[$f])) { + $validateList[$f] = $this->_fields[$f]; } } - $this->_validate = $validate; + } else { + return $this->_fields; } + + return $validateList; } /** @@ -469,7 +419,7 @@ class ModelValidator { * @param array $options * @return boolean */ - public function propagateBeforeValidate($options = array()) { + protected function _triggerBeforeValidate($options = array()) { $model = $this->getModel(); $event = new CakeEvent('Model.beforeValidate', $model, array($options)); list($event->break, $event->breakOn) = array(true, false); diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 3aed0c348..4a7f0a070 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1665,49 +1665,6 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals($expected, array_keys($result)); } -/** - * testSetOptions method - * - * @return void - */ - public function testSetOptions() { - $this->loadFixtures('Article', 'Comment'); - $TestModel = new Article(); - $Validator = $TestModel->validator(); - - $options = array('atomic' => false, 'validate' => true); - $Validator->setOptions($options); - $this->assertEquals($options, $Validator->options); - - $options = array('callbacks' => false); - $Validator->setOptions($options); - $this->assertEquals($options, $Validator->options); - - $options = array('atomic' => true); - $expected = array_merge($Validator->options, $options); - $Validator->setOptions($options, true); - $this->assertEquals($expected, $Validator->options); - } - -/** - * testGetOptions method - * - * @return void - */ - public function testGetOptions() { - $this->loadFixtures('Article', 'Comment'); - $TestModel = new Article(); - $Validator = $TestModel->validator(); - $options = array('atomic' => false, 'validate' => true); - $Validator->setOptions($options); - - $result = $Validator->getOptions(); - $this->assertEquals($options, $result); - - $result = $Validator->getOptions('atomic'); - $this->assertFalse($result); - } - /** * testSetValidationDomain method * From b6c8a345e7cf6b40aa3dcc7a4015d505209b3bb7 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 13:43:40 -0430 Subject: [PATCH 21/42] Renaming CakeField to CakeValidationSet --- lib/Cake/Model/ModelValidator.php | 17 +++++--------- lib/Cake/Model/Validator/CakeRule.php | 2 +- .../{CakeField.php => CakeValidationSet.php} | 4 ++-- lib/Cake/Test/Case/Model/ModelTest.php | 2 +- ...ieldTest.php => CakeValidationSetTest.php} | 22 +++++++++---------- 5 files changed, 20 insertions(+), 27 deletions(-) rename lib/Cake/Model/Validator/{CakeField.php => CakeValidationSet.php} (98%) rename lib/Cake/Test/Case/Model/Validator/{CakeFieldTest.php => CakeValidationSetTest.php} (87%) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index d425c7d57..c41aa24ef 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -18,7 +18,7 @@ * @since CakePHP(tm) v 2.2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -App::uses('CakeField', 'Model/Validator'); +App::uses('CakeValidationSet', 'Model/Validator'); App::uses('CakeRule', 'Model/Validator'); /** @@ -30,14 +30,7 @@ App::uses('CakeRule', 'Model/Validator'); class ModelValidator { /** - * Holds the validationErrors - * - * @var array - */ - public $validationErrors = array(); - -/** - * Holds the CakeField objects array + * Holds the CakeValidationSet objects array * * @var array */ @@ -279,7 +272,7 @@ class ModelValidator { * Gets all fields if $name is null (default), or the field for fieldname $name if it's found. * * @param string $name [optional] The fieldname to fetch. Defaults to null. - * @return mixed Either array of CakeField objects , single object for $name or false when $name not present in fields + * @return mixed Either array of CakeValidationSet objects , single object for $name or false when $name not present in fields */ public function getFields($name = null) { if ($name !== null && !empty($this->_fields[$name])) { @@ -291,7 +284,7 @@ class ModelValidator { } /** - * Sets the CakeField instances from the Model::$validate property after processing the fieldList and whiteList. + * Sets the CakeValidationSet instances from the Model::$validate property after processing the fieldList and whiteList. * If Model::$validate is not set or empty, this method returns false. True otherwise. * * @return boolean True if Model::$validate was processed, false otherwise @@ -311,7 +304,7 @@ class ModelValidator { $this->_fields = array(); $methods = $this->getMethods(); foreach ($this->_validate as $fieldName => $ruleSet) { - $this->_fields[$fieldName] = new CakeField($fieldName, $ruleSet, $methods); + $this->_fields[$fieldName] = new CakeValidationSet($fieldName, $ruleSet, $methods); } return true; } diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index f5b33253d..02bfd67e6 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -19,7 +19,7 @@ * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ App::uses('ModelValidator', 'Model'); -App::uses('CakeField', 'Model/Validator'); +App::uses('CakeValidationSet', 'Model/Validator'); App::uses('Validation', 'Utility'); /** diff --git a/lib/Cake/Model/Validator/CakeField.php b/lib/Cake/Model/Validator/CakeValidationSet.php similarity index 98% rename from lib/Cake/Model/Validator/CakeField.php rename to lib/Cake/Model/Validator/CakeValidationSet.php index 901007a68..7fb6d9c17 100644 --- a/lib/Cake/Model/Validator/CakeField.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -22,12 +22,12 @@ App::uses('ModelValidator', 'Model'); App::uses('CakeRule', 'Model/Validator'); /** - * CakeField object. + * CakeValidationSet object. * * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class CakeField { +class CakeValidationSet { /** * Holds the ValidationRule objects diff --git a/lib/Cake/Test/Case/Model/ModelTest.php b/lib/Cake/Test/Case/Model/ModelTest.php index e5e1567f2..3d3714842 100644 --- a/lib/Cake/Test/Case/Model/ModelTest.php +++ b/lib/Cake/Test/Case/Model/ModelTest.php @@ -34,7 +34,7 @@ class ModelTest extends PHPUnit_Framework_TestSuite { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('All Model related class tests'); - $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeFieldTest.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeValidationSetTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeRuleTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelReadTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelWriteTest.php'); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php similarity index 87% rename from lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php rename to lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index fdf064dbb..a68cddc3c 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeFieldTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -1,6 +1,6 @@ '', 'body' => 'a body' @@ -51,12 +51,12 @@ class CakeFieldTest extends CakeTestModel { $expected = array('This field cannot be left blank'); $this->assertEquals($expected, $result); - $Field = new CakeField('body', 'notEmpty'); + $Field = new CakeValidationSet('body', 'notEmpty'); $result = $Field->validate($data); $this->assertEmpty($result); - $Field = new CakeField('nothere', array('notEmpty' => array('rule' => 'notEmpty', 'required' => true))); + $Field = new CakeValidationSet('nothere', array('notEmpty' => array('rule' => 'notEmpty', 'required' => true))); $result = $Field->validate($data); $expected = array('notEmpty'); @@ -70,7 +70,7 @@ class CakeFieldTest extends CakeTestModel { */ public function testGetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField('title', $rules); + $Field = new CakeValidationSet('title', $rules); $data = array( 'title' => '', 'body' => 'a body' @@ -93,7 +93,7 @@ class CakeFieldTest extends CakeTestModel { */ public function testGetRules() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField('title', $rules); + $Field = new CakeValidationSet('title', $rules); $result = $Field->getRules(); $this->assertEquals(array('notEmpty'), array_keys($result)); @@ -107,7 +107,7 @@ class CakeFieldTest extends CakeTestModel { */ public function testSetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField('title', $rules); + $Field = new CakeValidationSet('title', $rules); $Rule = new CakeRule('notEmpty', $rules['notEmpty']); $this->assertEquals($Rule, $Field->getRule('notEmpty')); @@ -140,7 +140,7 @@ class CakeFieldTest extends CakeTestModel { */ public function testSetRules() { $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); - $Field = new CakeField('title', $rule); + $Field = new CakeValidationSet('title', $rule); $RuleEmpty = new CakeRule('title', $rule['notEmpty'], 'notEmpty'); $rule = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); From d35f8e6b006d37812669231a3f52b756a5a55b25 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 15:52:02 -0430 Subject: [PATCH 22/42] Implementing ArrayAccess in ModelValidator --- lib/Cake/Model/ModelValidator.php | 59 ++++++++++-- .../Test/Case/Model/ModelValidationTest.php | 93 +++++++++++++++++++ 2 files changed, 146 insertions(+), 6 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index c41aa24ef..692041954 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -18,8 +18,8 @@ * @since CakePHP(tm) v 2.2.0 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ + App::uses('CakeValidationSet', 'Model/Validator'); -App::uses('CakeRule', 'Model/Validator'); /** * ModelValidator object. @@ -27,7 +27,7 @@ App::uses('CakeRule', 'Model/Validator'); * @package Cake.Model * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class ModelValidator { +class ModelValidator implements ArrayAccess { /** * Holds the CakeValidationSet objects array @@ -290,16 +290,16 @@ class ModelValidator { * @return boolean True if Model::$validate was processed, false otherwise */ protected function _parseRules() { + if ($this->_validate === $this->_model->validate) { + return true; + } + if (empty($this->_model->validate)) { $this->_validate = array(); $this->_fields = array(); return false; } - if ($this->_validate === $this->_model->validate) { - return true; - } - $this->_validate = $this->_model->validate; $this->_fields = array(); $methods = $this->getMethods(); @@ -423,4 +423,51 @@ class ModelValidator { return true; } +/** + * Returns wheter a rule set is defined for a field or not + * + * @param string $field name of the field to check + * @return boolean + **/ + public function offsetExists($field) { + $this->_parseRules(); + return isset($this->_fields[$field]); + } + +/** + * Returns the rule set for a field + * + * @param string $field name of the field to check + * @return CakeValidationSet + **/ + public function offsetGet($field) { + $this->_parseRules(); + return $this->_fields[$field]; + } + +/** + * Sets the rule set for a field + * + * @param string $field name of the field to set + * @param array|CakeValidationSet $rules set of rules to apply to field + * @return void + **/ + public function offsetSet($field, $rules) { + $this->_parseRules(); + if (!$rules instanceof CakeValidationSet) { + $rules = new CakeValidationSet($field, $rules, $this->getMethods()); + } + $this->_fields[$field] = $rules; + } + +/** + * Unsets the rulset for a field + * + * @param string $field name of the field to unset + * @return void + **/ + public function offsetUnset($field) { + unset($this->_fields[$field]); + } + } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 4a7f0a070..0b5c84c95 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1696,4 +1696,97 @@ class ModelValidationTest extends BaseModelTest { $this->assertInstanceOf('Article', $result); } +/** + * Tests it is possible to get validation sets for a field using an array inteface + * + * @return void + */ + public function testArrayAccessGet() { + $this->loadFixtures('Article'); + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $titleValidator = $Validator['title']; + $this->assertEquals('title', $titleValidator->field); + $this->assertCount(1, $titleValidator->getRules()); + $rule = current($titleValidator->getRules()); + $this->assertEquals('notEmpty', $rule->rule); + + $titleValidator = $Validator['body']; + $this->assertEquals('body', $titleValidator->field); + $this->assertCount(1, $titleValidator->getRules()); + $rule = current($titleValidator->getRules()); + $this->assertEquals('notEmpty', $rule->rule); + + $titleValidator = $Validator['user_id']; + $this->assertEquals('user_id', $titleValidator->field); + $this->assertCount(1, $titleValidator->getRules()); + $rule = current($titleValidator->getRules()); + $this->assertEquals('numeric', $rule->rule); + } + +/** + * Tests it is possible to check for validation sets for a field using an array inteface + * + * @return void + */ + public function testArrayAccessExists() { + $this->loadFixtures('Article'); + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $this->assertTrue(isset($Validator['title'])); + $this->assertTrue(isset($Validator['body'])); + $this->assertTrue(isset($Validator['user_id'])); + $this->assertFalse(isset($Validator['other'])); + } + +/** + * Tests it is possible to set validation rules for a field using an array inteface + * + * @return void + */ + public function testArrayAccessSet() { + $this->loadFixtures('Article'); + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $set = array( + 'numeric' => array('rule' => 'numeric', 'allowEmpty' => false), + 'range' => array('rule' => array('between', 1, 5), 'allowEmpty' => false), + ); + $Validator['other'] = $set; + $rules = $Validator['other']; + $this->assertEquals('other', $rules->field); + + $validators = $rules->getRules(); + $this->assertCount(2, $validators); + $this->assertEquals('numeric', $validators['numeric']->rule); + $this->assertEquals(array('between', 1, 5), $validators['range']->rule); + + $Validator['new'] = new CakeValidationSet('new', $set, array()); + $rules = $Validator['new']; + $this->assertEquals('new', $rules->field); + + $validators = $rules->getRules(); + $this->assertCount(2, $validators); + $this->assertEquals('numeric', $validators['numeric']->rule); + $this->assertEquals(array('between', 1, 5), $validators['range']->rule); + } + +/** + * Tests it is possible to unset validation rules + * + * @return void + */ + public function testArrayAccessUset() { + $this->loadFixtures('Article'); + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $this->assertTrue(isset($Validator['title'])); + unset($Validator['title']); + $this->assertFalse(isset($Validator['title'])); + } + } From 81c0c3d91d05cab428cadd5983304aec15c726c1 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 17:34:35 -0430 Subject: [PATCH 23/42] Implementing IteratorAggregate interface for ModelValidator --- lib/Cake/Model/ModelValidator.php | 11 +++++++- .../Test/Case/Model/ModelValidationTest.php | 27 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 692041954..0116910ad 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -27,7 +27,7 @@ App::uses('CakeValidationSet', 'Model/Validator'); * @package Cake.Model * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class ModelValidator implements ArrayAccess { +class ModelValidator implements ArrayAccess, IteratorAggregate { /** * Holds the CakeValidationSet objects array @@ -470,4 +470,13 @@ class ModelValidator implements ArrayAccess { unset($this->_fields[$field]); } +/** + * Returns an iterator for each of the fields to be validated + * + * @return ArrayIterator + **/ + public function getIterator() { + $this->_parseRules(); + return new ArrayIterator($this->_fields); + } } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 0b5c84c95..b71d0858f 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1789,4 +1789,31 @@ class ModelValidationTest extends BaseModelTest { $this->assertFalse(isset($Validator['title'])); } +/** + * Tests it is possible to iterate a validation object + * + * @return void + */ + public function testIterator() { + $this->loadFixtures('Article'); + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $i = 0; + foreach ($Validator as $field => $rules) { + if ($i === 0) { + $this->assertEquals('user_id', $field); + } + if ($i === 1) { + $this->assertEquals('title', $field); + } + if ($i === 2) { + $this->assertEquals('body', $field); + } + $this->assertInstanceOf('CakeValidationSet', $rules); + $i++; + } + $this->assertEquals(3, $i); + } + } From 3199b9029bd085f3efb31174aa1000616d9e952e Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 17:46:09 -0430 Subject: [PATCH 24/42] Implemented countable interface for ModelValidator --- lib/Cake/Model/ModelValidator.php | 13 ++++++++- .../Test/Case/Model/ModelValidationTest.php | 29 +++++++++++++++---- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 0116910ad..9224b79fa 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -27,7 +27,7 @@ App::uses('CakeValidationSet', 'Model/Validator'); * @package Cake.Model * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class ModelValidator implements ArrayAccess, IteratorAggregate { +class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { /** * Holds the CakeValidationSet objects array @@ -479,4 +479,15 @@ class ModelValidator implements ArrayAccess, IteratorAggregate { $this->_parseRules(); return new ArrayIterator($this->_fields); } + +/** + * Returns the numbers of fields having validation rules + * + * @return int + **/ + public function count() { + $this->_parseRules(); + return count($this->_fields); + } + } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index b71d0858f..5ff65c0c6 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1688,7 +1688,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testGetModel() { - $this->loadFixtures('Article', 'Comment'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1702,7 +1701,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testArrayAccessGet() { - $this->loadFixtures('Article'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1731,7 +1729,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testArrayAccessExists() { - $this->loadFixtures('Article'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1747,7 +1744,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testArrayAccessSet() { - $this->loadFixtures('Article'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1780,7 +1776,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testArrayAccessUset() { - $this->loadFixtures('Article'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1795,7 +1790,6 @@ class ModelValidationTest extends BaseModelTest { * @return void */ public function testIterator() { - $this->loadFixtures('Article'); $TestModel = new Article(); $Validator = $TestModel->validator(); @@ -1816,4 +1810,27 @@ class ModelValidationTest extends BaseModelTest { $this->assertEquals(3, $i); } +/** + * Tests countable interface in ModelValidator + * + * @return void + */ + public function testCount() { + $TestModel = new Article(); + $Validator = $TestModel->validator(); + $this->assertCount(3, $Validator); + + $set = array( + 'numeric' => array('rule' => 'numeric', 'allowEmpty' => false), + 'range' => array('rule' => array('between', 1, 5), 'allowEmpty' => false), + ); + $Validator['other'] = $set; + $this->assertCount(4, $Validator); + + unset($Validator['title']); + $this->assertCount(3, $Validator); + unset($Validator['body']); + $this->assertCount(2, $Validator); + } + } From 877e6c0f66279027e53f173e2291f2697d3f2198 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 18:46:58 -0430 Subject: [PATCH 25/42] Removing the need for first param in CakeRule constructor --- lib/Cake/Model/Validator/CakeRule.php | 15 +-------------- lib/Cake/Model/Validator/CakeValidationSet.php | 11 ++++++----- .../Test/Case/Model/Validator/CakeRuleTest.php | 12 ++++++------ .../Model/Validator/CakeValidationSetTest.php | 10 +++++----- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeRule.php index 02bfd67e6..c65eb1271 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeRule.php @@ -37,13 +37,6 @@ class CakeRule { */ protected $_valid = true; -/** - * Holds the index under which the Validator was attached - * - * @var mixed - */ - protected $_index = null; - /** * Create or Update transaction? * @@ -118,10 +111,8 @@ class CakeRule { * Constructor * * @param array $validator [optional] The validator properties - * @param mixed $index [optional] */ - public function __construct($index = null, $validator = array()) { - $this->_index = $index; + public function __construct($validator = array()) { $this->_addValidatorProps($validator); } @@ -286,10 +277,6 @@ class CakeRule { return $this->_passedOptions[$key]; } - public function getName() { - return $this->_index; - } - /** * Sets the rule properties from the rule entry in validate * diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 7fb6d9c17..e36f56076 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -85,7 +85,7 @@ class CakeValidationSet { } foreach ($ruleSet as $index => $validateProp) { - $this->_rules[$index] = new CakeRule($index, $validateProp); + $this->_rules[$index] = new CakeRule($validateProp); } $this->ruleSet = $ruleSet; } @@ -116,7 +116,7 @@ class CakeValidationSet { */ public function validate($data, $isUpdate = false) { $errors = array(); - foreach ($this->getRules() as $rule) { + foreach ($this->getRules() as $name => $rule) { $rule->isUpdate($isUpdate); if ($rule->skip()) { continue; @@ -131,7 +131,7 @@ class CakeValidationSet { } if ($checkRequired || !$rule->isValid()) { - $errors[] = $this->_processValidationResponse($rule); + $errors[] = $this->_processValidationResponse($name, $rule); if ($rule->isLast()) { break; } @@ -193,11 +193,12 @@ class CakeValidationSet { /** * Fetches the correct error message for a failed validation * + * @param string $name the name of the rule as it was configured + * @param CakeRule $rule the object containing validation information * @return string */ - protected function _processValidationResponse($rule) { + protected function _processValidationResponse($name, $rule) { $message = $rule->getValidationResult(); - $name = $rule->getName(); if (is_string($message)) { return $message; } diff --git a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php index 0e79d4832..e96ce641d 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php @@ -74,7 +74,7 @@ class CakeRuleTest extends CakeTestCase { ); $methods = array(); - $Rule = new CakeRule('notEmpty', $def); + $Rule = new CakeRule($def); $Rule->process('fieldName', $data, $methods); $this->assertFalse($Rule->isValid()); @@ -94,7 +94,7 @@ class CakeRuleTest extends CakeTestCase { ); $methods = array('mytestrule' => array($this, 'myTestRule')); - $Rule = new CakeRule('custom', $def); + $Rule = new CakeRule($def); $Rule->process('fieldName', $data, $methods); $this->assertFalse($Rule->isValid()); @@ -114,19 +114,19 @@ class CakeRuleTest extends CakeTestCase { */ public function testIsRequired() { $def = array('rule' => 'notEmpty', 'required' => true); - $Rule = new CakeRule('required', $def); + $Rule = new CakeRule($def); $this->assertTrue($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => false); - $Rule = new CakeRule('required', $def); + $Rule = new CakeRule($def); $this->assertFalse($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => 'create'); - $Rule = new CakeRule('required', $def); + $Rule = new CakeRule($def); $this->assertTrue($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => 'update'); - $Rule = new CakeRule('required', $def); + $Rule = new CakeRule($def); $this->assertFalse($Rule->isRequired()); $Rule->isUpdate(true); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index a68cddc3c..3202cabd4 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -108,18 +108,18 @@ class CakeValidationSetTest extends CakeTestModel { public function testSetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); $Field = new CakeValidationSet('title', $rules); - $Rule = new CakeRule('notEmpty', $rules['notEmpty']); + $Rule = new CakeRule($rules['notEmpty']); $this->assertEquals($Rule, $Field->getRule('notEmpty')); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $Rule = new CakeRule('validEmail', $rules['validEmail']); + $Rule = new CakeRule($rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Other message')); - $Rule = new CakeRule('validEmail', $rules['validEmail']); + $Rule = new CakeRule($rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); @@ -141,10 +141,10 @@ class CakeValidationSetTest extends CakeTestModel { public function testSetRules() { $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); $Field = new CakeValidationSet('title', $rule); - $RuleEmpty = new CakeRule('title', $rule['notEmpty'], 'notEmpty'); + $RuleEmpty = new CakeRule($rule['notEmpty']); $rule = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $RuleEmail = new CakeRule('email', $rule['validEmail'], 'validEmail'); + $RuleEmail = new CakeRule($rule['validEmail']); $rules = array('validEmail' => $RuleEmail); $Field->setRules($rules, false); From 6f16a66b833176edd7a8e81c1a15dbe83fdd34d5 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 21:00:40 -0430 Subject: [PATCH 26/42] Implmenting ArrayAcces in CakeValidationSet --- lib/Cake/Model/ModelValidator.php | 1 + .../Model/Validator/CakeValidationSet.php | 51 ++++++++++- .../Model/Validator/CakeValidationSetTest.php | 89 ++++++++++++++++++- 3 files changed, 136 insertions(+), 5 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 9224b79fa..ca0936a37 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -467,6 +467,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * @return void **/ public function offsetUnset($field) { + $this->_parseRules(); unset($this->_fields[$field]); } diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index e36f56076..29d21c9cb 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -1,6 +1,6 @@ _rules[$key] = $rule; return $this; } @@ -241,4 +244,44 @@ class CakeValidationSet { return $message; } +/** + * Returns wheter an index exists in the rule set + * + * @param string $index name of the rule + * @return boolean + **/ + public function offsetExists($index) { + return isset($this->_rules[$index]); + } + +/** + * Returns a rule object by its index + * + * @param string $index name of the rule + * @return CakeRule + **/ + public function offsetGet($index) { + return $this->_rules[$index]; + } + +/** + * Sets or replace a validation rule + * + * @param string $index name of the rule + * @param CakeRule|array rule to add to $index + **/ + public function offsetSet($index, $rule) { + $this->setRule($index, $rule); + } + +/** + * Unsets a validation rule + * + * @param string $index name of the rule + * @return void + **/ + public function offsetUnset($index) { + unset($this->_rules[$index]); + } + } diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 3202cabd4..2ea5a3a41 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -24,7 +24,7 @@ App::uses('CakeValidationSet', 'Model/Validator'); * * @package Cake.Test.Case.Model.Validator */ -class CakeValidationSetTest extends CakeTestModel { +class CakeValidationSetTest extends CakeTestCase { /** * setUp method @@ -157,4 +157,91 @@ class CakeValidationSetTest extends CakeTestModel { $this->assertEquals(array('validEmail', 'notEmpty'), array_keys($result)); } +/** + * Tests getting a rule from the set using array access + * + * @return void + */ + public function testArrayAccessGet() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + + $rule = $Set['notEmpty']; + $this->assertInstanceOf('CakeRule', $rule); + $this->assertEquals('notEmpty', $rule->rule); + + $rule = $Set['numeric']; + $this->assertInstanceOf('CakeRule', $rule); + $this->assertEquals('numeric', $rule->rule); + + $rule = $Set['other']; + $this->assertInstanceOf('CakeRule', $rule); + $this->assertEquals(array('other', 1), $rule->rule); + } + +/** + * Tests checking a rule from the set using array access + * + * @return void + */ + public function testArrayAccessExists() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + + $this->assertTrue(isset($Set['notEmpty'])); + $this->assertTrue(isset($Set['numeric'])); + $this->assertTrue(isset($Set['other'])); + $this->assertFalse(isset($Set['fail'])); + } + +/** + * Tests setting a rule in the set using array access + * + * @return void + */ + public function testArrayAccessSet() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + )); + + $this->assertFalse(isset($Set['other'])); + $Set['other'] = array('rule' => array('other', 1)); + $rule = $Set['other']; + $this->assertInstanceOf('CakeRule', $rule); + $this->assertEquals(array('other', 1), $rule->rule); + + $this->assertFalse(isset($Set['numeric'])); + $Set['numeric'] = new CakeRule(array('rule' => 'numeric')); + $rule = $Set['numeric']; + $this->assertInstanceOf('CakeRule', $rule); + $this->assertEquals('numeric', $rule->rule); + } + +/** + * Tests unseting a rule from the set using array access + * + * @return void + */ + public function testArrayAccessUnset() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + + unset($Set['notEmpty']); + $this->assertFalse(isset($Set['notEmpty'])); + + unset($Set['numeric']); + $this->assertFalse(isset($Set['notEmpty'])); + + unset($Set['other']); + $this->assertFalse(isset($Set['notEmpty'])); + } } From 989a8b83983b831050429dfd409f293380990c6c Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 5 May 2012 21:13:09 -0430 Subject: [PATCH 27/42] Implementing IteratorAggregate for CakeValidationSet --- .../Model/Validator/CakeValidationSet.php | 13 ++++++-- .../Model/Validator/CakeValidationSetTest.php | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 29d21c9cb..f3c29b62d 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -27,7 +27,7 @@ App::uses('CakeRule', 'Model/Validator'); * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class CakeValidationSet implements ArrayAccess { +class CakeValidationSet implements ArrayAccess, IteratorAggregate { /** * Holds the ValidationRule objects @@ -284,4 +284,13 @@ class CakeValidationSet implements ArrayAccess { unset($this->_rules[$index]); } -} +/** + * Returns an iterator for each of the rules to be applied + * + * @return ArrayIterator + **/ + public function getIterator() { + return new ArrayIterator($this->_rules); + } + +} \ No newline at end of file diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 2ea5a3a41..ce5fcd5ef 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -244,4 +244,34 @@ class CakeValidationSetTest extends CakeTestCase { unset($Set['other']); $this->assertFalse(isset($Set['notEmpty'])); } + +/** + * Tests it is possible to iterate a validation set object + * + * @return void + */ + public function testIterator() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + + $i = 0; + foreach ($Set as $name => $rule) { + if ($i === 0) { + $this->assertEquals('notEmpty', $name); + } + if ($i === 1) { + $this->assertEquals('numeric', $name); + } + if ($i === 2) { + $this->assertEquals('other', $name); + } + $this->assertInstanceOf('CakeRule', $rule); + $i++; + } + $this->assertEquals(3, $i); + } + } From ff91a0909af6fdcad0fcfe8dc526c299c8431079 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 6 May 2012 01:07:13 -0430 Subject: [PATCH 28/42] Implemented countable interface for CakeValidationSet --- lib/Cake/Model/ModelValidator.php | 2 +- lib/Cake/Model/Validator/CakeValidationSet.php | 11 ++++++++++- .../Model/Validator/CakeValidationSetTest.php | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index ca0936a37..4c29b1078 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -482,7 +482,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Returns the numbers of fields having validation rules + * Returns the number of fields having validation rules * * @return int **/ diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index f3c29b62d..1fde937ee 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -27,7 +27,7 @@ App::uses('CakeRule', 'Model/Validator'); * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html */ -class CakeValidationSet implements ArrayAccess, IteratorAggregate { +class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { /** * Holds the ValidationRule objects @@ -293,4 +293,13 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate { return new ArrayIterator($this->_rules); } +/** + * Returns the number of rules in this set + * + * @return int + **/ + public function count() { + return count($this->_rules); + } + } \ No newline at end of file diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index ce5fcd5ef..0314cc4df 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -274,4 +274,21 @@ class CakeValidationSetTest extends CakeTestCase { $this->assertEquals(3, $i); } +/** + * Tests countable interface + * + * @return void + */ + public function testCount() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + $this->assertCount(3, $Set); + + unset($Set['other']); + $this->assertCount(2, $Set); + } + } From 1ff1af35b38364e23ed3fa3cc7b68dbdf93112bc Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 6 May 2012 20:45:50 -0430 Subject: [PATCH 29/42] Fixing typo --- lib/Cake/Model/ModelValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 4c29b1078..5460f81a8 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -261,7 +261,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { $methods[strtolower($method)] = array($this->_model, $method); } - foreach (array_keys($this->_model->Behaviors->methods()) as $mthod) { + foreach (array_keys($this->_model->Behaviors->methods()) as $method) { $methods += array(strtolower($method) => array($this->_model, $method)); } From a7222bccd615a776c4c860eff02d5a88a70506ef Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 6 May 2012 22:30:07 -0430 Subject: [PATCH 30/42] Implemented removeRule in CakeValidationSet --- .../Model/Validator/CakeValidationSet.php | 13 ++++++++++- .../Model/Validator/CakeValidationSetTest.php | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 1fde937ee..4b7bb1f35 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -167,7 +167,7 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { * * @param mixed $key The key under which the rule should be set * @param CakeRule|array $rule The validation rule to be set - * @return ModelField + * @return CakeValidationSet this instance */ public function setRule($key, $rule) { if (!$rule instanceof CakeRule) { @@ -177,6 +177,17 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { return $this; } +/** + * Removes a validation rule from the set + * + * @param mixed $key The key under which the rule should be unset + * @return CakeValidationSet this instance + */ + public function removeRule($key) { + unset($this->_rules[$key]); + return $this; + } + /** * Sets the rules for a given field * diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 0314cc4df..91af4be65 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -291,4 +291,26 @@ class CakeValidationSetTest extends CakeTestCase { $this->assertCount(2, $Set); } +/** + * Test removeRule method + * + * @return void + */ + public function testRemoveRule() { + $Set = new CakeValidationSet('title', array( + 'notEmpty' => array('rule' => 'notEmpty', 'required' => true), + 'numeric' => array('rule' => 'numeric'), + 'other' => array('rule' => array('other', 1)), + )); + + $Set->removeRule('notEmpty'); + $this->assertFalse(isset($Set['notEmpty'])); + + $Set->removeRule('numeric'); + $this->assertFalse(isset($Set['numeric'])); + + $Set->removeRule('other'); + $this->assertFalse(isset($Set['other'])); + } + } From d4511af27b60ba3d8d3040bdc1ffb34e29093a3e Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 6 May 2012 23:06:28 -0430 Subject: [PATCH 31/42] Implemented add and remove in ModelValidator --- lib/Cake/Model/ModelValidator.php | 34 ++++++++++++++ .../Test/Case/Model/ModelValidationTest.php | 47 +++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 5460f81a8..ba84d3b67 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -491,4 +491,38 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { return count($this->_fields); } +/** + * Adds a new rule to a field's rule set + * + * @param string $field The name of the field from wich the rule will be removed + * @param array|CakeRule $rule the rule to be added to the field's rule set + * @return ModelValidator this instance + **/ + public function add($field, $name, $rule) { + $this->_parseRules(); + if (!isset($this->_fields[$field])) { + $rule = array($name => $rule); + $this->_fields[$field] = new CakeValidationSet($field, $rule, $this->getMethods()); + } else { + $this->_fields[$field]->setRule($name, $rule); + } + return $this; + } + +/** + * Removes a rule from the set by its name + * + * @param string $field The name of the field from wich the rule will be removed + * @param string $rule the name of the rule to be removed + * @return ModelValidator this instance + **/ + public function remove($field, $rule = null) { + $this->_parseRules(); + if ($rule === null) { + unset($this->_fields[$field]); + } else { + $this->_fields[$field]->removeRule($rule); + } + return $this; + } } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 5ff65c0c6..9adff1c80 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1833,4 +1833,51 @@ class ModelValidationTest extends BaseModelTest { $this->assertCount(2, $Validator); } +/** + * Tests it is possible to add validation rules + * + * @return void + */ + public function testAddRule() { + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $set = array( + 'numeric' => array('rule' => 'numeric', 'allowEmpty' => false), + 'range' => array('rule' => array('between', 1, 5), 'allowEmpty' => false), + ); + + $Validator->add('other', 'numeric', array('rule' => 'numeric', 'allowEmpty' => false)); + $Validator->add('other', 'range', array('rule' => array('between', 1, 5), 'allowEmpty' => false)); + $rules = $Validator['other']; + $this->assertEquals('other', $rules->field); + + $validators = $rules->getRules(); + $this->assertCount(2, $validators); + $this->assertEquals('numeric', $validators['numeric']->rule); + $this->assertEquals(array('between', 1, 5), $validators['range']->rule); + } + +/** + * Tests it is possible to remove validation rules + * + * @return void + */ + public function testRemoveRule() { + $TestModel = new Article(); + $Validator = $TestModel->validator(); + + $this->assertTrue(isset($Validator['title'])); + $Validator->remove('title'); + $this->assertFalse(isset($Validator['title'])); + + $Validator->add('other', 'numeric', array('rule' => 'numeric', 'allowEmpty' => false)); + $Validator->add('other', 'range', array('rule' => array('between', 1, 5), 'allowEmpty' => false)); + $this->assertTrue(isset($Validator['other'])); + + $Validator->remove('other', 'numeric'); + $this->assertTrue(isset($Validator['other'])); + $this->assertFalse(isset($Validator['other']['numeric'])); + $this->assertTrue(isset($Validator['other']['range'])); + } } From 52c7b2383c5b435476a401792417948badedd045 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sun, 6 May 2012 23:38:29 -0430 Subject: [PATCH 32/42] Added afterValidate callback to maintain consistency across all other callbacks in the model layer --- lib/Cake/Model/Model.php | 9 +++++++++ lib/Cake/Model/ModelValidator.php | 1 + lib/Cake/Test/Case/Model/ModelValidationTest.php | 15 +++++++++++++++ .../Model/Validator/CakeValidationSetTest.php | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 97b364543..5dc04a134 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -744,6 +744,7 @@ class Model extends Object implements CakeEventListener { 'Model.beforeFind' => array('callable' => 'beforeFind', 'passParams' => true), 'Model.afterFind' => array('callable' => 'afterFind', 'passParams' => true), 'Model.beforeValidate' => array('callable' => 'beforeValidate', 'passParams' => true), + 'Model.afterValidate' => array('callable' => 'afterValidate'), 'Model.beforeSave' => array('callable' => 'beforeSave', 'passParams' => true), 'Model.afterSave' => array('callable' => 'afterSave', 'passParams' => true), 'Model.beforeDelete' => array('callable' => 'beforeDelete', 'passParams' => true), @@ -3311,6 +3312,14 @@ class Model extends Object implements CakeEventListener { return true; } +/** + * Called after data has been checked for errors + * + * @return void + */ + public function afterValidate() { + } + /** * Called when a DataSource-level error occurs. * diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index ba84d3b67..5728a7947 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -229,6 +229,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } } + $model->getEventManager()->dispatch(new CakeEvent('Model.afterValidate', $model)); return $model->validationErrors; } diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 9adff1c80..2fe7780f8 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1880,4 +1880,19 @@ class ModelValidationTest extends BaseModelTest { $this->assertFalse(isset($Validator['other']['numeric'])); $this->assertTrue(isset($Validator['other']['range'])); } + +/** + * Tests validation callbacks are triggered + * + * @return void + */ + public function testValidateCallbacks() { + $TestModel = $this->getMock('Article', array('beforeValidate', 'afterValidate')); + $TestModel->expects($this->once())->method('beforeValidate'); + $TestModel->expects($this->once())->method('afterValidate'); + + $TestModel->set(array('title' => '', 'body' => 'body')); + $TestModel->validates(); + } + } diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 91af4be65..5bb66f149 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -313,4 +313,4 @@ class CakeValidationSetTest extends CakeTestCase { $this->assertFalse(isset($Set['other'])); } -} +} \ No newline at end of file From 280893155a6b56f46b5e59ebae89843ae2c1c3a6 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 21:01:44 -0430 Subject: [PATCH 33/42] Renaming CakeRule to CakeValidationRule --- lib/Cake/Model/ModelValidator.php | 2 +- .../{CakeRule.php => CakeValidationRule.php} | 6 ++-- .../Model/Validator/CakeValidationSet.php | 16 +++++----- lib/Cake/Test/Case/Model/ModelTest.php | 2 +- ...uleTest.php => CakeValidationRuleTest.php} | 20 ++++++------- .../Model/Validator/CakeValidationSetTest.php | 30 +++++++++---------- 6 files changed, 38 insertions(+), 38 deletions(-) rename lib/Cake/Model/Validator/{CakeRule.php => CakeValidationRule.php} (98%) rename lib/Cake/Test/Case/Model/Validator/{CakeRuleTest.php => CakeValidationRuleTest.php} (87%) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 5728a7947..18f3c9754 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -496,7 +496,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * Adds a new rule to a field's rule set * * @param string $field The name of the field from wich the rule will be removed - * @param array|CakeRule $rule the rule to be added to the field's rule set + * @param array|CakeValidationRule $rule the rule to be added to the field's rule set * @return ModelValidator this instance **/ public function add($field, $name, $rule) { diff --git a/lib/Cake/Model/Validator/CakeRule.php b/lib/Cake/Model/Validator/CakeValidationRule.php similarity index 98% rename from lib/Cake/Model/Validator/CakeRule.php rename to lib/Cake/Model/Validator/CakeValidationRule.php index c65eb1271..d50d30ac8 100644 --- a/lib/Cake/Model/Validator/CakeRule.php +++ b/lib/Cake/Model/Validator/CakeValidationRule.php @@ -1,6 +1,6 @@ $validateProp) { - $this->_rules[$index] = new CakeRule($validateProp); + $this->_rules[$index] = new CakeValidationRule($validateProp); } $this->ruleSet = $ruleSet; } @@ -166,12 +166,12 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { * Sets a ValidationRule $rule for key $key * * @param mixed $key The key under which the rule should be set - * @param CakeRule|array $rule The validation rule to be set + * @param CakeValidationRule|array $rule The validation rule to be set * @return CakeValidationSet this instance */ public function setRule($key, $rule) { - if (!$rule instanceof CakeRule) { - $rule = new CakeRule($rule); + if (!$rule instanceof CakeValidationRule) { + $rule = new CakeValidationRule($rule); } $this->_rules[$key] = $rule; return $this; @@ -208,7 +208,7 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { * Fetches the correct error message for a failed validation * * @param string $name the name of the rule as it was configured - * @param CakeRule $rule the object containing validation information + * @param CakeValidationRule $rule the object containing validation information * @return string */ protected function _processValidationResponse($name, $rule) { @@ -269,7 +269,7 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { * Returns a rule object by its index * * @param string $index name of the rule - * @return CakeRule + * @return CakeValidationRule **/ public function offsetGet($index) { return $this->_rules[$index]; @@ -279,7 +279,7 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { * Sets or replace a validation rule * * @param string $index name of the rule - * @param CakeRule|array rule to add to $index + * @param CakeValidationRule|array rule to add to $index **/ public function offsetSet($index, $rule) { $this->setRule($index, $rule); diff --git a/lib/Cake/Test/Case/Model/ModelTest.php b/lib/Cake/Test/Case/Model/ModelTest.php index 3d3714842..2cde6b920 100644 --- a/lib/Cake/Test/Case/Model/ModelTest.php +++ b/lib/Cake/Test/Case/Model/ModelTest.php @@ -35,7 +35,7 @@ class ModelTest extends PHPUnit_Framework_TestSuite { $suite = new PHPUnit_Framework_TestSuite('All Model related class tests'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeValidationSetTest.php'); - $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeRuleTest.php'); + $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'Validator' . DS .'CakeValidationRuleTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelReadTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelWriteTest.php'); $suite->addTestFile(CORE_TEST_CASES . DS . 'Model' . DS . 'ModelDeleteTest.php'); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php similarity index 87% rename from lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php rename to lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php index e96ce641d..c72f4969f 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php @@ -1,6 +1,6 @@ process('fieldName', $data, $methods); $this->assertFalse($Rule->isValid()); @@ -94,7 +94,7 @@ class CakeRuleTest extends CakeTestCase { ); $methods = array('mytestrule' => array($this, 'myTestRule')); - $Rule = new CakeRule($def); + $Rule = new CakeValidationRule($def); $Rule->process('fieldName', $data, $methods); $this->assertFalse($Rule->isValid()); @@ -114,19 +114,19 @@ class CakeRuleTest extends CakeTestCase { */ public function testIsRequired() { $def = array('rule' => 'notEmpty', 'required' => true); - $Rule = new CakeRule($def); + $Rule = new CakeValidationRule($def); $this->assertTrue($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => false); - $Rule = new CakeRule($def); + $Rule = new CakeValidationRule($def); $this->assertFalse($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => 'create'); - $Rule = new CakeRule($def); + $Rule = new CakeValidationRule($def); $this->assertTrue($Rule->isRequired()); $def = array('rule' => 'notEmpty', 'required' => 'update'); - $Rule = new CakeRule($def); + $Rule = new CakeValidationRule($def); $this->assertFalse($Rule->isRequired()); $Rule->isUpdate(true); diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php index 5bb66f149..689e3e023 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationSetTest.php @@ -77,7 +77,7 @@ class CakeValidationSetTest extends CakeTestCase { ); $result = $Field->getRule('notEmpty'); - $this->assertInstanceOf('CakeRule', $result); + $this->assertInstanceOf('CakeValidationRule', $result); $this->assertEquals('notEmpty', $result->rule); $this->assertEquals(null, $result->required); $this->assertEquals(false, $result->allowEmpty); @@ -97,7 +97,7 @@ class CakeValidationSetTest extends CakeTestCase { $result = $Field->getRules(); $this->assertEquals(array('notEmpty'), array_keys($result)); - $this->assertInstanceOf('CakeRule', $result['notEmpty']); + $this->assertInstanceOf('CakeValidationRule', $result['notEmpty']); } /** @@ -108,23 +108,23 @@ class CakeValidationSetTest extends CakeTestCase { public function testSetRule() { $rules = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); $Field = new CakeValidationSet('title', $rules); - $Rule = new CakeRule($rules['notEmpty']); + $Rule = new CakeValidationRule($rules['notEmpty']); $this->assertEquals($Rule, $Field->getRule('notEmpty')); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $Rule = new CakeRule($rules['validEmail']); + $Rule = new CakeValidationRule($rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); $rules = array('validEmail' => array('rule' => 'email', 'message' => 'Other message')); - $Rule = new CakeRule($rules['validEmail']); + $Rule = new CakeValidationRule($rules['validEmail']); $Field->setRule('validEmail', $Rule); $result = $Field->getRules(); $this->assertEquals(array('notEmpty', 'validEmail'), array_keys($result)); $result = $Field->getRule('validEmail'); - $this->assertInstanceOf('CakeRule', $result); + $this->assertInstanceOf('CakeValidationRule', $result); $this->assertEquals('email', $result->rule); $this->assertEquals(null, $result->required); $this->assertEquals(false, $result->allowEmpty); @@ -141,10 +141,10 @@ class CakeValidationSetTest extends CakeTestCase { public function testSetRules() { $rule = array('notEmpty' => array('rule' => 'notEmpty', 'message' => 'Can not be empty')); $Field = new CakeValidationSet('title', $rule); - $RuleEmpty = new CakeRule($rule['notEmpty']); + $RuleEmpty = new CakeValidationRule($rule['notEmpty']); $rule = array('validEmail' => array('rule' => 'email', 'message' => 'Invalid email')); - $RuleEmail = new CakeRule($rule['validEmail']); + $RuleEmail = new CakeValidationRule($rule['validEmail']); $rules = array('validEmail' => $RuleEmail); $Field->setRules($rules, false); @@ -170,15 +170,15 @@ class CakeValidationSetTest extends CakeTestCase { )); $rule = $Set['notEmpty']; - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $this->assertEquals('notEmpty', $rule->rule); $rule = $Set['numeric']; - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $this->assertEquals('numeric', $rule->rule); $rule = $Set['other']; - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $this->assertEquals(array('other', 1), $rule->rule); } @@ -213,13 +213,13 @@ class CakeValidationSetTest extends CakeTestCase { $this->assertFalse(isset($Set['other'])); $Set['other'] = array('rule' => array('other', 1)); $rule = $Set['other']; - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $this->assertEquals(array('other', 1), $rule->rule); $this->assertFalse(isset($Set['numeric'])); - $Set['numeric'] = new CakeRule(array('rule' => 'numeric')); + $Set['numeric'] = new CakeValidationRule(array('rule' => 'numeric')); $rule = $Set['numeric']; - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $this->assertEquals('numeric', $rule->rule); } @@ -268,7 +268,7 @@ class CakeValidationSetTest extends CakeTestCase { if ($i === 2) { $this->assertEquals('other', $name); } - $this->assertInstanceOf('CakeRule', $rule); + $this->assertInstanceOf('CakeValidationRule', $rule); $i++; } $this->assertEquals(3, $i); From 8c9c6b4a5ab02015f4239b90b286e767f00e6c81 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 21:27:01 -0430 Subject: [PATCH 34/42] Improving docs for ModelValidator --- lib/Cake/Model/ModelValidator.php | 82 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 18f3c9754..9c2bb28bb 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -7,12 +7,12 @@ * PHP versions 5 * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * - * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package Cake.Model * @since CakePHP(tm) v 2.2.0 @@ -22,7 +22,11 @@ App::uses('CakeValidationSet', 'Model/Validator'); /** - * ModelValidator object. + * ModelValidator object encapsulates all methods related to data validations for a model + * It also provides an API to dynamically change validation rules for each model field. + * + * Implements ArrayAccess to easily modify rules as usually done with `Model::$validate` + * definition array * * @package Cake.Model * @link http://book.cakephp.org/2.0/en/data-validation.html @@ -37,21 +41,23 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { protected $_fields = array(); /** - * Holds the reference to the model the Validator is attached to + * Holds the reference to the model this Validator is attached to * * @var Model */ protected $_model = array(); /** - * The validators $validate property + * The validators $validate property, used for checking wheter validation + * rules definition changed in the model and should be refreshed in this class * * @var array */ protected $_validate = array(); /** - * Holds the available custom callback methods + * Holds the available custom callback methods, usually taken from model methods + * and behavior methods * * @var array */ @@ -68,9 +74,9 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { /** * 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. + * that use the 'with' key as well. Since `Model::_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. + * Will validate the currently set data. Use `Model::set()` or `Model::create()` to set the active data. * * @param array $options An optional array of custom options to be made available in the beforeValidate callback * @return boolean True if there are no errors @@ -198,11 +204,12 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Returns an array of fields that have failed validation. On the current model. + * Returns an array of fields that have failed validation. On the current model. This method will + * actually run validation rules over data, not just return the messages. * * @param string $options An optional array of custom options to be made available in the beforeValidate callback * @return array Array of invalid fields - * @see Model::validates() + * @see ModelValidator::validates() */ public function errors($options = array()) { if (!$this->_triggerBeforeValidate($options)) { @@ -234,23 +241,22 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Marks a field as invalid, optionally setting the name of validation - * rule (in case of multiple validation for field) that was broken. + * Marks a field as invalid, optionally setting a message explaining + * why the rule failed * * @param string $field The name of the field to invalidate - * @param string $value Name of validation rule that failed, or validation message to - * be returned. If no validation key is provided, defaults to true. + * @param string $message Validation message explaining why the rule failed, defaults to true. * @return void */ - public function invalidate($field, $value = true) { + public function invalidate($field, $message = true) { $this->getModel()->validationErrors[$field][] = $value; } /** - * Gets all possible custom methods from the Model, Behaviors and the Validator. - * gets the corresponding methods. + * Gets all possible custom methods from the Model and attached Behaviors + * to be used as validators * - * @return array The requested methods + * @return array List of callables to be used as validation methods */ public function getMethods() { if (!empty($this->_methods)) { @@ -270,25 +276,26 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Gets all fields if $name is null (default), or the field for fieldname $name if it's found. + * Returns a CakeValidationSet object containing all validation rules for a field, if no + * params are passed then it returns an array with all CakeValidationSet objects for each field * * @param string $name [optional] The fieldname to fetch. Defaults to null. - * @return mixed Either array of CakeValidationSet objects , single object for $name or false when $name not present in fields + * @return CakeValidationSet|array */ - public function getFields($name = null) { + public function getField($name = null) { if ($name !== null && !empty($this->_fields[$name])) { return $this->_fields[$name]; } elseif ($name !==null) { - return false; + return null } return $this->_fields; } /** - * Sets the CakeValidationSet instances from the Model::$validate property after processing the fieldList and whiteList. - * If Model::$validate is not set or empty, this method returns false. True otherwise. + * Sets the CakeValidationSet objects from the `Model::$validate` property + * If `Model::$validate` is not set or empty, this method returns false. True otherwise. * - * @return boolean True if Model::$validate was processed, false otherwise + * @return boolean true if `Model::$validate` was processed, false otherwise */ protected function _parseRules() { if ($this->_validate === $this->_model->validate) { @@ -325,7 +332,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Gets the parent Model + * Gets the model related to this validator * * @return Model */ @@ -334,9 +341,10 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Processes the Model's whitelist and returns the list of fields + * Processes the Model's whitelist or passed fieldList and returns the list of fields * to be validated * + * @param array $fieldList list of fields to be used for validation * @return array List of validation rules to be applied */ protected function _validationList($fieldList = array()) { @@ -370,7 +378,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { /** * Runs validation for hasAndBelongsToMany associations that have 'with' keys - * set. And data in the set() data set. + * set and data in the data set. * * @param array $options Array of options to use on Validation of with models * @return boolean Failure of validation on with models. @@ -408,7 +416,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } /** - * Propagates the beforeValidate event + * Propagates beforeValidate event * * @param array $options * @return boolean @@ -495,6 +503,14 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { /** * Adds a new rule to a field's rule set * + * ## Example: + * + * {{{ + * $validator + * ->add('title', 'required', array('rule' => 'notEmpty', 'required' => true)) + * ->add('user_id', 'valid', array('rule' => 'numeric', 'message' => 'Invalid User')) + * }}} + * * @param string $field The name of the field from wich the rule will be removed * @param array|CakeValidationRule $rule the rule to be added to the field's rule set * @return ModelValidator this instance @@ -513,6 +529,14 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { /** * Removes a rule from the set by its name * + * ## Example: + * + * {{{ + * $validator + * ->remove('title', 'required') + * ->remove('user_id') + * }}} + * * @param string $field The name of the field from wich the rule will be removed * @param string $rule the name of the rule to be removed * @return ModelValidator this instance From c4202b7b22eecc9605c52d070cd5d07b3acf9d04 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 21:39:53 -0430 Subject: [PATCH 35/42] Improving docs for CakeValidationSet --- .../Model/Validator/CakeValidationSet.php | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 7892637df..0aea4bb01 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -30,14 +30,14 @@ App::uses('CakeValidationRule', 'Model/Validator'); class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { /** - * Holds the ValidationRule objects + * Holds the CakeValidationRule objects * * @var array */ protected $_rules = array(); /** - * Set of methods available for validation + * List of methods available for validation * * @var array **/ @@ -51,7 +51,7 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { protected $_validationDomain = null; /** - * If the validation is stopped + * Whether the validation is stopped * * @var boolean */ @@ -110,7 +110,8 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { } /** - * Validates a ModelField + * Runs all validation rules in this set and returns a list of + * validation errors * * @return array list of validation errors for this field */ @@ -142,19 +143,19 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { } /** - * Gets a rule for a certain index + * Gets a rule for a given name if exists * - * @param mixed index - * @return ValidationRule + * @param string $name + * @return CakeValidationRule */ - public function getRule($index) { - if (!empty($this->_rules[$index])) { - return $this->_rules[$index]; + public function getRule($name) { + if (!empty($this->_rules[$name])) { + return $this->_rules[$name]; } } /** - * Gets all rules for this ModelField + * Returns all rules for this validation set * * @return array */ @@ -163,34 +164,59 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable { } /** - * Sets a ValidationRule $rule for key $key + * Sets a CakeValidationRule $rule with a $name * - * @param mixed $key The key under which the rule should be set + * ## Example: + * + * {{{ + * $set + * ->setRule('required', array('rule' => 'notEmpty', 'required' => true)) + * ->setRule('inRange', array('rule' => array('between', 4, 10)) + * }}} + * + * @param mixed $name The name under which the rule should be set * @param CakeValidationRule|array $rule The validation rule to be set * @return CakeValidationSet this instance */ - public function setRule($key, $rule) { + public function setRule($name, $rule) { if (!$rule instanceof CakeValidationRule) { $rule = new CakeValidationRule($rule); } - $this->_rules[$key] = $rule; + $this->_rules[$name] = $rule; return $this; } /** * Removes a validation rule from the set * - * @param mixed $key The key under which the rule should be unset + * ## Example: + * + * {{{ + * $set + * ->removeRule('required') + * ->removeRule('inRange') + * }}} + * + * @param mixed $name The name under which the rule should be unset * @return CakeValidationSet this instance */ - public function removeRule($key) { - unset($this->_rules[$key]); + public function removeRule($name) { + unset($this->_rules[$name]); return $this; } /** * Sets the rules for a given field * + * ## Example: + * + * {{{ + * $set->setRules(array( + * 'required' => array('rule' => 'notEmpty', 'required' => true), + * 'inRange' => array('rule' => array('between', 4, 10) + * )); + * }}} + * * @param array $rules The rules to be set * @param bolean $mergeVars [optional] If true, merges vars instead of replace. Defaults to true. * @return ModelField From 34531d9d7c8a880b815ae22082fba38d9fc4017d Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 21:41:42 -0430 Subject: [PATCH 36/42] Cahnging doc in header for CakeValidationSet --- lib/Cake/Model/Validator/CakeValidationSet.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeValidationSet.php b/lib/Cake/Model/Validator/CakeValidationSet.php index 0aea4bb01..b407008e3 100644 --- a/lib/Cake/Model/Validator/CakeValidationSet.php +++ b/lib/Cake/Model/Validator/CakeValidationSet.php @@ -7,12 +7,12 @@ * PHP versions 5 * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * - * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package Cake.Model.Validator * @since CakePHP(tm) v 2.2.0 @@ -22,7 +22,8 @@ App::uses('ModelValidator', 'Model'); App::uses('CakeValidationRule', 'Model/Validator'); /** - * CakeValidationSet object. + * CakeValidationSet object. Holds all validation rules for a field and exposes + * methods to dynamically add or remove validation rules * * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html From 2b7c2ad691ced1d0b51bdc7689aa1d8a78363f29 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 21:51:54 -0430 Subject: [PATCH 37/42] Fixing doc blocks for CakeValidationRule and changing visibility for some methods --- .../Model/Validator/CakeValidationRule.php | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/Cake/Model/Validator/CakeValidationRule.php b/lib/Cake/Model/Validator/CakeValidationRule.php index d50d30ac8..1963de4b4 100644 --- a/lib/Cake/Model/Validator/CakeValidationRule.php +++ b/lib/Cake/Model/Validator/CakeValidationRule.php @@ -7,12 +7,12 @@ * PHP versions 5 * * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) - * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * * Licensed under The MIT License * Redistributions of files must retain the above copyright notice. * - * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org) + * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package Cake.Model.Validator * @since CakePHP(tm) v 2.2.0 @@ -23,7 +23,8 @@ App::uses('CakeValidationSet', 'Model/Validator'); App::uses('Validation', 'Utility'); /** - * CakeValidationRule object. + * CakeValidationRule object. Represents a validation method, error message and + * rules for applying such method to a field. * * @package Cake.Model.Validator * @link http://book.cakephp.org/2.0/en/data-validation.html @@ -31,28 +32,28 @@ App::uses('Validation', 'Utility'); class CakeValidationRule { /** - * The 'valid' value + * Whether the field passed this validation rule * * @var mixed */ protected $_valid = true; /** - * Create or Update transaction? + * Holds whether the record being validated exists in datasource or not * * @var boolean */ protected $_recordExists = false; /** - * The parsed rule + * Validation method * * @var mixed */ protected $_rule = null; /** - * The parsed rule parameters + * Validation method arguments * * @var array */ @@ -130,7 +131,7 @@ class CakeValidationRule { } /** - * Checks if the field is required by the 'required' value + * Checks if the field is required according to the `required` property * * @return boolean */ @@ -147,7 +148,7 @@ class CakeValidationRule { } /** - * Checks if the field failed the required validation + * Checks whether the field failed the `field should be present` validation * * @param array $data data to check rule against * @return boolean @@ -176,7 +177,7 @@ class CakeValidationRule { } /** - * Checks if the Validation rule can be skipped + * Checks if the validation rule should be skipped * * @return boolean True if the ValidationRule can be skipped */ @@ -190,7 +191,8 @@ class CakeValidationRule { } /** - * Checks if the 'last' key is true + * Returns whethere this rule should break validation process for associated field + * after it fails * * @return boolean */ @@ -212,7 +214,7 @@ class CakeValidationRule { * * @return array */ - public function getPropertiesArray() { + protected function _getPropertiesArray() { $rule = $this->rule; if (!is_string($rule)) { unset($rule[0]); @@ -250,9 +252,10 @@ class CakeValidationRule { * @return boolean True if the rule could be dispatched, false otherwise */ public function process($field, &$data, &$methods) { + $this->_valid = true; $this->_parseRule($field, $data); - $validator = $this->getPropertiesArray(); + $validator = $this->_getPropertiesArray(); $rule = strtolower($this->_rule); if (isset($methods[$rule])) { $this->_ruleParams[] = array_merge($validator, $this->_passedOptions); @@ -270,6 +273,11 @@ class CakeValidationRule { return true; } +/** + * Returns passed options for this rule + * + * @return array + **/ public function getOptions($key) { if (!isset($this->_passedOptions[$key])) { return null; From efc3536adb7ac75723a4b345ac36792340cfca23 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 22:04:55 -0430 Subject: [PATCH 38/42] Fixing a couple errors introduced in previous commit --- lib/Cake/Model/ModelValidator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 9c2bb28bb..e0a5d8306 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -249,7 +249,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * @return void */ public function invalidate($field, $message = true) { - $this->getModel()->validationErrors[$field][] = $value; + $this->getModel()->validationErrors[$field][] = $message; } /** @@ -286,7 +286,7 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { if ($name !== null && !empty($this->_fields[$name])) { return $this->_fields[$name]; } elseif ($name !==null) { - return null + return null; } return $this->_fields; } From 785cf8354c4b6074562381870dbd34159f2600a7 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 22:36:55 -0430 Subject: [PATCH 39/42] Fixing merge artifacts to make all tests pass --- lib/Cake/Model/Model.php | 62 ++----------------------------- lib/Cake/Model/ModelValidator.php | 28 +++++++++++--- 2 files changed, 26 insertions(+), 64 deletions(-) diff --git a/lib/Cake/Model/Model.php b/lib/Cake/Model/Model.php index 62ac03e18..9de8f322a 100644 --- a/lib/Cake/Model/Model.php +++ b/lib/Cake/Model/Model.php @@ -2120,7 +2120,7 @@ class Model extends Object implements CakeEventListener { * Otherwise: array similar to the $data array passed, but values are set to true/false * depending on whether each record validated successfully. */ - public function validateMany($data, $options = array()) { + public function validateMany(&$data, $options = array()) { return $this->validator()->validateMany($data, $options); } @@ -2300,63 +2300,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, 'deep' => false), $options); - $this->validationErrors = $validationErrors = $return = array(); - if (!($this->create($data) && $this->validates($options))) { - $validationErrors[$this->alias] = $this->validationErrors; - $return[$this->alias] = false; - } else { - $return[$this->alias] = true; - } - - if (empty($options['deep'])) { - $data = $this->data; - } else { - $modelData = $this->data; - $recordData = $modelData[$this->alias]; - unset($modelData[$this->alias]); - $data = $modelData + array_merge($data, $recordData); - } - - $associations = $this->getAssociated(); - foreach ($data as $association => &$values) { - $validates = true; - if (isset($associations[$association])) { - if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { - 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; - } - if (!$validates || (is_array($validates) && in_array(false, $validates, true))) { - $validationErrors[$association] = $this->{$association}->validationErrors; - } - } - } - - $this->validationErrors = $validationErrors; - if (isset($validationErrors[$this->alias])) { - $this->validationErrors = $validationErrors[$this->alias]; - } - if (!$options['atomic']) { - return $return; - } - if ($return[$this->alias] === false || !empty($this->validationErrors)) { - return false; - } - return true; + return $this->validator()->validateAssociated($data, $options); } /** @@ -3432,7 +3376,7 @@ class Model extends Object implements CakeEventListener { return $this->_validator = $instance; } - if (is_null($instance) && is_null($this->validatorClass)) { + if (is_null($instance)) { $this->_validator = new ModelValidator($this); } diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index e0a5d8306..be8d15921 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -101,15 +101,18 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * - 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. * + * Warning: This method could potentially change the passed argument `$data`, + * If you do not want this to happen, make a copy of `$data` before passing it + * to this method + * * @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(). * @return array|boolean 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 validated successfully. */ - public function validateAssociated($data, $options = array()) { + public function validateAssociated(&$data, $options = array()) { $model = $this->getModel(); - $options = array_merge(array('atomic' => true, 'deep' => false), $options); $model->validationErrors = $validationErrors = $return = array(); if (!($model->create($data) && $model->validates($options))) { @@ -118,8 +121,18 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { } else { $return[$model->alias] = true; } + + if (empty($options['deep'])) { + $data = $model->data; + } else { + $modelData = $model->data; + $recordData = $modelData[$model->alias]; + unset($modelData[$model->alias]); + $data = $modelData + array_merge($data, $recordData); + } + $associations = $model->getAssociated(); - foreach ($data as $association => $values) { + foreach ($data as $association => &$values) { $validates = true; if (isset($associations[$association])) { if (in_array($associations[$association], array('belongsTo', 'hasOne'))) { @@ -168,6 +181,10 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * - fieldList: Equivalent to the $fieldList parameter in Model::save() * - deep: If set to true, all associated data will be validated as well. * + * Warning: This method could potentially change the passed argument `$data`, + * If you do not want this to happen, make a copy of `$data` before passing it + * to this method + * * @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(). * @return boolean True on success, or false on failure. @@ -175,15 +192,16 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { * Otherwise: array similar to the $data array passed, but values are set to true/false * depending on whether each record validated successfully. */ - public function validateMany($data, $options = array()) { + public function validateMany(&$data, $options = array()) { $model = $this->getModel(); $options = array_merge(array('atomic' => true, 'deep' => false), $options); $model->validationErrors = $validationErrors = $return = array(); - foreach ($data as $key => $record) { + foreach ($data as $key => &$record) { if ($options['deep']) { $validates = $model->validateAssociated($record, $options); } else { $validates = $model->create($record) && $model->validates($options); + $data[$key] = $model->data; } if ($validates === false || (is_array($validates) && in_array(false, $validates, true))) { $validationErrors[$key] = $model->validationErrors; From 217bf85537f213b7b281ceeab2929fe9f891b6c1 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 23:13:45 -0430 Subject: [PATCH 40/42] Implemented new method isEmptyAllowed in CakeValidationRule --- .../Model/Validator/CakeValidationRule.php | 9 ++++++ .../Validator/CakeValidationRuleTest.php | 29 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/Cake/Model/Validator/CakeValidationRule.php b/lib/Cake/Model/Validator/CakeValidationRule.php index 1963de4b4..ab95b5693 100644 --- a/lib/Cake/Model/Validator/CakeValidationRule.php +++ b/lib/Cake/Model/Validator/CakeValidationRule.php @@ -130,6 +130,15 @@ class CakeValidationRule { return true; } +/** + * Returns whether the field can be left blank according to this rule + * + * @return boolean + */ + public function isEmptyAllowed() { + return $this->skip() || $this->allowEmpty === true; + } + /** * Checks if the field is required according to the `required` property * diff --git a/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php index c72f4969f..85d2350e5 100644 --- a/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php +++ b/lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php @@ -132,4 +132,33 @@ class CakeValidationRuleTest extends CakeTestCase { $Rule->isUpdate(true); $this->assertTrue($Rule->isRequired()); } + +/** + * Test isEmptyAllowed method + * + * @return void + */ + public function testIsEmplyAllowed() { + $def = array('rule' => 'aRule', 'allowEmpty' => true); + $Rule = new CakeValidationRule($def); + $this->assertTrue($Rule->isEmptyAllowed()); + + $def = array('rule' => 'aRule', 'allowEmpty' => false); + $Rule = new CakeValidationRule($def); + $this->assertFalse($Rule->isEmptyAllowed()); + + $def = array('rule' => 'notEmpty', 'allowEmpty' => false, 'on' => 'update'); + $Rule = new CakeValidationRule($def); + $this->assertTrue($Rule->isEmptyAllowed()); + + $Rule->isUpdate(true); + $this->assertFalse($Rule->isEmptyAllowed()); + + $def = array('rule' => 'notEmpty', 'allowEmpty' => false, 'on' => 'create'); + $Rule = new CakeValidationRule($def); + $this->assertFalse($Rule->isEmptyAllowed()); + + $Rule->isUpdate(true); + $this->assertTrue($Rule->isEmptyAllowed()); + } } From fe659c0c337b666c4e84a58ced726d5dd4e410b3 Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Thu, 10 May 2012 23:14:26 -0430 Subject: [PATCH 41/42] Refactored FormHelper to take advantage of the new validator object --- lib/Cake/View/Helper/FormHelper.php | 60 ++++------------------------- 1 file changed, 8 insertions(+), 52 deletions(-) diff --git a/lib/Cake/View/Helper/FormHelper.php b/lib/Cake/View/Helper/FormHelper.php index 28f573562..2c7701034 100644 --- a/lib/Cake/View/Helper/FormHelper.php +++ b/lib/Cake/View/Helper/FormHelper.php @@ -218,7 +218,7 @@ class FormHelper extends AppHelper { if ($key === 'validates' && !isset($this->fieldset[$model]['validates'])) { $validates = array(); if (!empty($object->validate)) { - foreach ($object->validate as $validateField => $validateProperties) { + foreach ($object->validator() as $validateField => $validateProperties) { if ($this->_isRequiredField($validateProperties)) { $validates[$validateField] = true; } @@ -240,61 +240,17 @@ class FormHelper extends AppHelper { /** * Returns if a field is required to be filled based on validation properties from the validating object. * - * @param array $validateProperties + * @param CakeValidationSet $validationRules * @return boolean true if field is required to be filled, false otherwise */ - protected function _isRequiredField($validateProperties) { - $required = false; - if (is_string($validateProperties)) { - return true; - } elseif (is_array($validateProperties)) { - - $dims = Hash::dimensions($validateProperties); - if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) { - $validateProperties = array($validateProperties); - } - - foreach ($validateProperties as $rule => $validateProp) { - $isRequired = $this->_isRequiredRule($validateProp); - if ($isRequired === false) { - continue; - } - $rule = isset($validateProp['rule']) ? $validateProp['rule'] : false; - $required = $rule || empty($validateProp); - if ($required) { - break; - } + protected function _isRequiredField($validationRules) { + foreach ($validationRules as $rule) { + $rule->isUpdate($this->requestType === 'put'); + if (!$rule->isEmptyAllowed()) { + return true; } } - return $required; - } - -/** - * Checks if the field is required by the 'on' key in validation properties. - * If no 'on' key is present in validation props, this method returns true. - * - * @param array $validateProp - * @return mixed. Boolean for required - */ - protected function _isRequiredRule($validateProp) { - if (isset($validateProp['on'])) { - if ( - ($validateProp['on'] == 'create' && $this->requestType != 'post') || - ($validateProp['on'] == 'update' && $this->requestType != 'put') - ) { - return false; - } - } - if ( - isset($validateProp['allowEmpty']) && - $validateProp['allowEmpty'] === true - ) { - return false; - } - if (isset($validateProp['required']) && empty($validateProp['required'])) { - return false; - } - return true; + return false; } /** From dc8c731ccbecda51c4d332d50eda1ade084cd2ef Mon Sep 17 00:00:00 2001 From: Jose Lorenzo Rodriguez Date: Sat, 19 May 2012 19:59:32 -0430 Subject: [PATCH 42/42] Fixed failing test case --- lib/Cake/Model/ModelValidator.php | 5 +++++ lib/Cake/Test/Case/Model/ModelValidationTest.php | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Cake/Model/ModelValidator.php b/lib/Cake/Model/ModelValidator.php index 7bb95d869..83c9c6b8d 100644 --- a/lib/Cake/Model/ModelValidator.php +++ b/lib/Cake/Model/ModelValidator.php @@ -123,6 +123,11 @@ class ModelValidator implements ArrayAccess, IteratorAggregate, Countable { $return[$model->alias] = true; } $data = $model->data; + if (!empty($options['deep']) && isset($data[$model->alias])) { + $recordData = $data[$model->alias]; + unset($data[$model->alias]); + $data = array_merge($data, $recordData); + } $associations = $model->getAssociated(); foreach ($data as $association => &$values) { diff --git a/lib/Cake/Test/Case/Model/ModelValidationTest.php b/lib/Cake/Test/Case/Model/ModelValidationTest.php index 530dd96b2..962c736ad 100644 --- a/lib/Cake/Test/Case/Model/ModelValidationTest.php +++ b/lib/Cake/Test/Case/Model/ModelValidationTest.php @@ -1564,7 +1564,8 @@ class ModelValidationTest extends BaseModelTest { 'comment' => 'comment', 'published' => 'Y', - 'user_id' => 1), + 'user_id' => 1 + ), array( 'id' => 3, 'comment' => '',