Skip to content

Commit

Permalink
Implemented rule processing from the default scope
Browse files Browse the repository at this point in the history
  • Loading branch information
lorenzo committed Nov 20, 2013
1 parent 0a3960d commit 0aab99e
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 179 deletions.
139 changes: 23 additions & 116 deletions Cake/ORM/Validation/ValidationRule.php
Expand Up @@ -26,13 +26,6 @@
*/
class ValidationRule {

/**
* Validation method
*
* @var mixed
*/
protected $_rule = null;

/**
* Validation method arguments
*
Expand All @@ -41,39 +34,32 @@ class ValidationRule {
protected $_ruleParams = array();

/**
* Holds passed in options
* The method to be called for a given scope
*
* @var array
* @var string|\Closure
*/
protected $_passedOptions = array();

/**
* The 'rule' key
*
* @var mixed
*/
public $rule = 'blank';
protected $_rule;

/**
* The 'on' key
*
* @var string
*/
public $on = null;
protected $_on = null;

/**
* The 'last' key
*
* @var boolean
*/
public $last = true;
protected $_last = true;

/**
* The 'message' key
*
* @var string
*/
public $message = null;
protected $_message = null;

/**
* Constructor
Expand All @@ -84,19 +70,6 @@ public function __construct($validator = array()) {
$this->_addValidatorProps($validator);
}

/**
* Checks if the rule is valid
*
* @return boolean
*/
public function isValid() {
if (!$this->_valid || (is_string($this->_valid) && !empty($this->_valid))) {
return false;
}

return true;
}

/**
* Checks if the validation rule should be skipped
*
Expand All @@ -122,72 +95,25 @@ public function isLast() {
}

/**
* Gets the validation error message
*
* @return string
*/
public function getValidationResult() {
return $this->_valid;
}

/**
* Gets an array with the rule properties
* Dispatches the validation rule to the given validator method and returns
* a boolean indicating whether the rule passed or not. If a string is returned
* it is assumed that the rule failed and the error message was given as a result.
*
* @return array
* @param mixed $data The data to validate
* @param array $scopes associative array with objects or class names that will
* be passed as the last argument for the validation method
* @param boolean $newRecord whether or not the data to be validated belongs to
* a new record
* @return boolean|string
*/
protected function _getPropertiesArray() {
$rule = $this->rule;
if (!is_string($rule)) {
unset($rule[0]);
public function process($data, $scopes, $newRecord) {
$scope = $scopes['default'];
$callable = [$scope, $this->_rule];
$result = $callable($data, $scopes);
if ($result === false) {
return $this->_message ?: false;
}
return array(
'rule' => $rule,
'on' => $this->on,
'last' => $this->last,
'message' => $this->message
);
}

/**
* Dispatches the validation rule to the given validator method
*
* @param string $field Field name
* @param array $data Data array
* @param array $methods Methods list
* @return boolean True if the rule could be dispatched, false otherwise
*/
public function process($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($field => $this->_ruleParams[0]);
$this->_valid = call_user_func_array($methods[$rule], $this->_ruleParams);
} elseif (method_exists('Cake\Utility\Validation', $this->_rule)) {
$this->_valid = call_user_func_array(array('Cake\Utility\Validation', $this->_rule), $this->_ruleParams);
} elseif (is_string($validator['rule'])) {
$this->_valid = preg_match($this->_rule, $data[$field]);
} else {
trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $field), E_USER_WARNING);
return false;
}

return true;
}

/**
* Returns passed options for this rule
*
* @param string|integer $key Array index
* @return array
*/
public function getOptions($key) {
if (!isset($this->_passedOptions[$key])) {
return null;
}
return $this->_passedOptions[$key];
return $result;
}

/**
Expand All @@ -203,29 +129,10 @@ protected function _addValidatorProps($validator = array()) {
foreach ($validator as $key => $value) {
if (isset($value) || !empty($value)) {
if (in_array($key, array('rule', 'on', 'message', 'last'))) {
$this->{$key} = $validator[$key];
} else {
$this->_passedOptions[$key] = $value;
$this->{"_$key"} = $validator[$key];
}
}
}
}

/**
* Parses the rule and sets the rule and ruleParams
*
* @param string $field Field name
* @param array $data Data array
* @return void
*/
protected function _parseRule($field, $data) {
if (is_array($this->rule)) {
$this->_rule = $this->rule[0];
$this->_ruleParams = array_merge(array($data[$field]), array_values(array_slice($this->rule, 1)));
} else {
$this->_rule = $this->rule;
$this->_ruleParams = array($data[$field]);
}
}

}
58 changes: 0 additions & 58 deletions Cake/ORM/Validation/ValidationSet.php
Expand Up @@ -190,64 +190,6 @@ public function remove($name) {
return $this;
}

/**
* Fetches the correct error message for a failed validation
*
* @param string $name the name of the rule as it was configured
* @param Cake\Model\Validator\ValidationRule $rule the object containing validation information
* @return string
*/
protected function _processValidationResponse($name, $rule) {
$message = $rule->getValidationResult();
if (is_string($message)) {
return $message;
}
$message = $rule->message;

if ($message !== null) {
$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);
}
$args = $this->_translateArgs($args);

$message = __d($this->_validationDomain, $result, $args);
} elseif (is_string($name)) {
if (is_array($rule->rule)) {
$args = array_slice($rule->rule, 1);
$args = $this->_translateArgs($args);
$message = __d($this->_validationDomain, $name, $args);
} else {
$message = __d($this->_validationDomain, $name);
}
} else {
$message = __d('cake', 'The provided value is invalid');
}

return $message;
}

/**
* Applies translations to validator arguments.
*
* @param array $args The args to translate
* @return array Translated args.
*/
protected function _translateArgs($args) {
foreach ((array)$args as $k => $arg) {
if (is_string($arg)) {
$args[$k] = __d($this->_validationDomain, $arg);
}
}
return $args;
}

/**
* Returns whether an index exists in the rule set
*
Expand Down
45 changes: 40 additions & 5 deletions Cake/ORM/Validator.php
Expand Up @@ -63,13 +63,31 @@ public function errors(array $data, $newRecord = true) {
$errors = [];
foreach ($this->_fields as $name => $field) {
$keyPresent = array_key_exists($name, $data);

if (!$keyPresent && !$this->_checkPresence($field, $newRecord)) {
$errors[$name][] = __d('cake', 'This field is required');
continue;
}

if (!$keyPresent) {
continue;
}
if ($keyPresent && !$this->_checkEmpty($field, $newRecord)) {
if ($this->_fieldIsEmpty($data[$name])) {
$errors[$name][] = __d('cake', 'This field cannot be left empty');
}

$canBeEmpty = $this->_canBeEmpty($field, $newRecord);
$isEmpty = $this->_fieldIsEmpty($data[$name]);

if (!$canBeEmpty && $isEmpty) {
$errors[$name][] = __d('cake', 'This field cannot be left empty');
continue;
}

if ($isEmpty) {
continue;
}

$result = $this->_processRules($field, $data[$name], $newRecord);
if ($result) {
$errors[$name] = $result;
}
}

Expand Down Expand Up @@ -308,7 +326,7 @@ protected function _checkPresence($field, $newRecord) {
* @param boolean $newRecord whether the data to be validated is new or to be updated.
* @return boolean
*/
protected function _checkEmpty($field, $newRecord) {
protected function _canBeEmpty($field, $newRecord) {
$allowed = $field->isEmptyAllowed();
if (in_array($allowed, array('create', 'update'), true)) {
$allowed = (
Expand All @@ -333,4 +351,21 @@ protected function _fieldIsEmpty($data) {
return false;
}

protected function _processRules(ValidationSet $rules, $value, $newRecord) {
$errors = [];
$this->scope('default'); // Loading default scope in case there is none
foreach ($rules as $name => $rule) {
$result = $rule->process($value, $this->_scopes, $newRecord);
if ($result === true) {
continue;
}

$errors[$name] = __d('cake', 'The provided value is invalid');
if (is_string($result)) {
$errors[$name] = __d($this->_validationDomain, $result);
}
}
return $errors;
}

}
22 changes: 22 additions & 0 deletions Cake/Test/TestCase/ORM/ValidatorTest.php
Expand Up @@ -225,4 +225,26 @@ public function testScope() {
$this->assertEquals('\Cake\Utility\Validation', $validator->scope('default'));
}

/**
* Tests errors() method when using validators from the default scope, this proves
* that it returns a default validation message and the custom one set in the rule
*
* @return void
*/
public function testErrorsFromDefaultScope() {
$validator = new Validator;
$validator
->add('email', 'alpha', ['rule' => 'alphanumeric'])
->add('email', 'notEmpty', ['rule' => 'notEmpty'])
->add('email', 'email', ['rule' => 'email', 'message' => 'Y u no write email?']);
$errors = $validator->errors(['email' => 'not an email!']);
$expected = [
'email' => [
'alpha' => 'The provided value is invalid',
'email' => 'Y u no write email?'
]
];
$this->assertEquals($expected, $errors);
}

}

0 comments on commit 0aab99e

Please sign in to comment.