General refactor on ModelValidation to decrease class coupling and increase readability

This commit is contained in:
Jose Lorenzo Rodriguez 2012-04-28 23:25:05 -04:30
parent 573d292ffa
commit b83f936e83
3 changed files with 194 additions and 268 deletions

View file

@ -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;
}
}

View file

@ -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]);
}
}