mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-01-31 09:06:17 +00:00
Moving all validation logic into a new ModelValidator object.
This commit is contained in:
parent
061483d03e
commit
324684c14f
8 changed files with 1259 additions and 311 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
543
lib/Cake/Model/ModelValidator.php
Normal file
543
lib/Cake/Model/ModelValidator.php
Normal file
|
@ -0,0 +1,543 @@
|
|||
<?php
|
||||
/**
|
||||
* ModelValidator.
|
||||
*
|
||||
* Provides the Model validation logic.
|
||||
*
|
||||
* PHP versions 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2011, 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)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Model
|
||||
* @since CakePHP(tm) v 0.10.0.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
App::uses('CakeField', 'Model/Validator');
|
||||
App::uses('CakeRule', 'Model/Validator');
|
||||
|
||||
/**
|
||||
* ModelValidator object.
|
||||
*
|
||||
* @package Cake.Model
|
||||
* @link http://book.cakephp.org/2.0/en/data-validation.html
|
||||
*/
|
||||
class ModelValidator {
|
||||
|
||||
/**
|
||||
* The default ModelValidator class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const DEFAULT_VALIDATOR = '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
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $validationErrors = array();
|
||||
|
||||
/**
|
||||
* Holds the options
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $options = array();
|
||||
|
||||
/**
|
||||
* Holds the ModelFields
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_fields = array();
|
||||
|
||||
/**
|
||||
* Holds the reference to the model the Validator is attached to
|
||||
*
|
||||
* @var Model
|
||||
*/
|
||||
protected $_model = array();
|
||||
|
||||
/**
|
||||
* The validators $validate property
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_validate = array();
|
||||
|
||||
/**
|
||||
* Holds the available custom callback methods
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_methods = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Model $Model A reference to the Model the Validator is attached to
|
||||
*/
|
||||
public function __construct(Model $Model) {
|
||||
$this->_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;
|
||||
}
|
||||
|
||||
}
|
188
lib/Cake/Model/Validator/CakeField.php
Normal file
188
lib/Cake/Model/Validator/CakeField.php
Normal file
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
/**
|
||||
* ModelValidator.
|
||||
*
|
||||
* Provides the Model validation logic.
|
||||
*
|
||||
* PHP versions 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2011, 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)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Model
|
||||
* @since CakePHP(tm) v 3.0.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
App::uses('ModelValidator', 'Model');
|
||||
App::uses('CakeRule', 'Model/Validator');
|
||||
|
||||
/**
|
||||
* ModelField object.
|
||||
*
|
||||
* @package Cake.Model
|
||||
* @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
|
||||
*/
|
||||
public function __construct(ModelValidator $validator, $fieldName, $ruleSet) {
|
||||
$this->_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());
|
||||
}
|
||||
|
||||
}
|
404
lib/Cake/Model/Validator/CakeRule.php
Normal file
404
lib/Cake/Model/Validator/CakeRule.php
Normal file
|
@ -0,0 +1,404 @@
|
|||
<?php
|
||||
/**
|
||||
* CakeRule.
|
||||
*
|
||||
* Provides the Model validation logic.
|
||||
*
|
||||
* PHP versions 5
|
||||
*
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright 2005-2011, 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)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @package Cake.Model
|
||||
* @since CakePHP(tm) v 3.0.0
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
App::uses('ModelValidator', 'Model');
|
||||
App::uses('CakeField', 'Model/Validator');
|
||||
App::uses('Validation', 'Utility');
|
||||
|
||||
/**
|
||||
* ValidationRule object.
|
||||
*
|
||||
* @package Cake.Model
|
||||
* @link http://book.cakephp.org/2.0/en/data-validation.html
|
||||
*/
|
||||
class CakeRule {
|
||||
|
||||
/**
|
||||
* Holds a reference to the parent field
|
||||
*
|
||||
* @var ModelField
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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 array $validator [optional] The validator properties
|
||||
* @param mixed $index [optional]
|
||||
*/
|
||||
public function __construct(CakeField $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -387,7 +387,7 @@ class AnotherTestController extends ControllerTestAppController {
|
|||
|
||||
/**
|
||||
* merge parent
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_mergeParent = 'ControllerTestAppController';
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue