pictcode / lib / Cake / Model / ModelValidator.php @ b3a58ce1
履歴 | 表示 | アノテート | ダウンロード (18.189 KB)
| 1 | 635eef61 | spyder1211 | <?php
 | 
      
|---|---|---|---|
| 2 | /**
 | 
      ||
| 3 |  * ModelValidator.
 | 
      ||
| 4 |  *
 | 
      ||
| 5 |  * Provides the Model validation logic.
 | 
      ||
| 6 |  *
 | 
      ||
| 7 |  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 | 
      ||
| 8 |  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 9 |  *
 | 
      ||
| 10 |  * Licensed under The MIT License
 | 
      ||
| 11 |  * For full copyright and license information, please see the LICENSE.txt
 | 
      ||
| 12 |  * Redistributions of files must retain the above copyright notice.
 | 
      ||
| 13 |  *
 | 
      ||
| 14 |  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 | 
      ||
| 15 |  * @link          http://cakephp.org CakePHP(tm) Project
 | 
      ||
| 16 |  * @package       Cake.Model
 | 
      ||
| 17 |  * @since         CakePHP(tm) v 2.2.0
 | 
      ||
| 18 |  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 | 
      ||
| 19 |  */
 | 
      ||
| 20 | |||
| 21 | App::uses('CakeValidationSet', 'Model/Validator');  | 
      ||
| 22 | App::uses('Hash', 'Utility');  | 
      ||
| 23 | |||
| 24 | /**
 | 
      ||
| 25 |  * ModelValidator object encapsulates all methods related to data validations for a model
 | 
      ||
| 26 |  * It also provides an API to dynamically change validation rules for each model field.
 | 
      ||
| 27 |  *
 | 
      ||
| 28 |  * Implements ArrayAccess to easily modify rules as usually done with `Model::$validate`
 | 
      ||
| 29 |  * definition array
 | 
      ||
| 30 |  *
 | 
      ||
| 31 |  * @package       Cake.Model
 | 
      ||
| 32 |  * @link          http://book.cakephp.org/2.0/en/data-validation.html
 | 
      ||
| 33 |  */
 | 
      ||
| 34 | class ModelValidator implements ArrayAccess, IteratorAggregate, Countable {  | 
      ||
| 35 | |||
| 36 | /**
 | 
      ||
| 37 |  * Holds the CakeValidationSet objects array
 | 
      ||
| 38 |  *
 | 
      ||
| 39 |  * @var array
 | 
      ||
| 40 |  */
 | 
      ||
| 41 | protected $_fields = array();  | 
      ||
| 42 | |||
| 43 | /**
 | 
      ||
| 44 |  * Holds the reference to the model this Validator is attached to
 | 
      ||
| 45 |  *
 | 
      ||
| 46 |  * @var Model
 | 
      ||
| 47 |  */
 | 
      ||
| 48 | protected $_model = array();  | 
      ||
| 49 | |||
| 50 | /**
 | 
      ||
| 51 |  * The validators $validate property, used for checking whether validation
 | 
      ||
| 52 |  * rules definition changed in the model and should be refreshed in this class
 | 
      ||
| 53 |  *
 | 
      ||
| 54 |  * @var array
 | 
      ||
| 55 |  */
 | 
      ||
| 56 | protected $_validate = array();  | 
      ||
| 57 | |||
| 58 | /**
 | 
      ||
| 59 |  * Holds the available custom callback methods, usually taken from model methods
 | 
      ||
| 60 |  * and behavior methods
 | 
      ||
| 61 |  *
 | 
      ||
| 62 |  * @var array
 | 
      ||
| 63 |  */
 | 
      ||
| 64 | protected $_methods = array();  | 
      ||
| 65 | |||
| 66 | /**
 | 
      ||
| 67 |  * Holds the available custom callback methods from the model
 | 
      ||
| 68 |  *
 | 
      ||
| 69 |  * @var array
 | 
      ||
| 70 |  */
 | 
      ||
| 71 | protected $_modelMethods = array();  | 
      ||
| 72 | |||
| 73 | /**
 | 
      ||
| 74 |  * Holds the list of behavior names that were attached when this object was created
 | 
      ||
| 75 |  *
 | 
      ||
| 76 |  * @var array
 | 
      ||
| 77 |  */
 | 
      ||
| 78 | protected $_behaviors = array();  | 
      ||
| 79 | |||
| 80 | /**
 | 
      ||
| 81 |  * Constructor
 | 
      ||
| 82 |  *
 | 
      ||
| 83 |  * @param Model $Model A reference to the Model the Validator is attached to
 | 
      ||
| 84 |  */
 | 
      ||
| 85 | public function __construct(Model $Model) {  | 
      ||
| 86 | $this->_model = $Model;  | 
      ||
| 87 | }  | 
      ||
| 88 | |||
| 89 | /**
 | 
      ||
| 90 |  * Returns true if all fields pass validation. Will validate hasAndBelongsToMany associations
 | 
      ||
| 91 |  * that use the 'with' key as well. Since `Model::_saveMulti` is incapable of exiting a save operation.
 | 
      ||
| 92 |  *
 | 
      ||
| 93 |  * Will validate the currently set data. Use `Model::set()` or `Model::create()` to set the active data.
 | 
      ||
| 94 |  *
 | 
      ||
| 95 |  * @param array $options An optional array of custom options to be made available in the beforeValidate callback
 | 
      ||
| 96 |  * @return bool True if there are no errors
 | 
      ||
| 97 |  */
 | 
      ||
| 98 | public function validates($options = array()) {  | 
      ||
| 99 | $errors = $this->errors($options);  | 
      ||
| 100 | if (empty($errors) && $errors !== false) {  | 
      ||
| 101 | $errors = $this->_validateWithModels($options);  | 
      ||
| 102 | }  | 
      ||
| 103 | if (is_array($errors)) {  | 
      ||
| 104 | return count($errors) === 0;  | 
      ||
| 105 | }  | 
      ||
| 106 | return $errors;  | 
      ||
| 107 | }  | 
      ||
| 108 | |||
| 109 | /**
 | 
      ||
| 110 |  * Validates a single record, as well as all its directly associated records.
 | 
      ||
| 111 |  *
 | 
      ||
| 112 |  * #### Options
 | 
      ||
| 113 |  *
 | 
      ||
| 114 |  * - atomic: If true (default), returns boolean. If false returns array.
 | 
      ||
| 115 |  * - fieldList: Equivalent to the $fieldList parameter in Model::save()
 | 
      ||
| 116 |  * - deep: If set to true, not only directly associated data , but deeper nested associated data is validated as well.
 | 
      ||
| 117 |  *
 | 
      ||
| 118 |  * Warning: This method could potentially change the passed argument `$data`,
 | 
      ||
| 119 |  * If you do not want this to happen, make a copy of `$data` before passing it
 | 
      ||
| 120 |  * to this method
 | 
      ||
| 121 |  *
 | 
      ||
| 122 |  * @param array &$data Record data to validate. This should be an array indexed by association name.
 | 
      ||
| 123 |  * @param array $options Options to use when validating record data (see above), See also $options of validates().
 | 
      ||
| 124 |  * @return array|bool If atomic: True on success, or false on failure.
 | 
      ||
| 125 |  *    Otherwise: array similar to the $data array passed, but values are set to true/false
 | 
      ||
| 126 |  *    depending on whether each record validated successfully.
 | 
      ||
| 127 |  */
 | 
      ||
| 128 | public function validateAssociated(&$data, $options = array()) {  | 
      ||
| 129 | $model = $this->getModel();  | 
      ||
| 130 | $options += array('atomic' => true, 'deep' => false);  | 
      ||
| 131 | $model->validationErrors = $validationErrors = $return = array();  | 
      ||
| 132 | $model->create(null);  | 
      ||
| 133 | $return[$model->alias] = true;  | 
      ||
| 134 | if (!($model->set($data) && $model->validates($options))) {  | 
      ||
| 135 | $validationErrors[$model->alias] = $model->validationErrors;  | 
      ||
| 136 | $return[$model->alias] = false;  | 
      ||
| 137 | }  | 
      ||
| 138 | $data = $model->data;  | 
      ||
| 139 | if (!empty($options['deep']) && isset($data[$model->alias])) {  | 
      ||
| 140 | $recordData = $data[$model->alias];  | 
      ||
| 141 | unset($data[$model->alias]);  | 
      ||
| 142 | $data += $recordData;  | 
      ||
| 143 | }  | 
      ||
| 144 | |||
| 145 | $associations = $model->getAssociated();  | 
      ||
| 146 | foreach ($data as $association => &$values) {  | 
      ||
| 147 | $validates = true;  | 
      ||
| 148 | if (isset($associations[$association])) {  | 
      ||
| 149 | if (in_array($associations[$association], array('belongsTo', 'hasOne'))) {  | 
      ||
| 150 | if ($options['deep']) {  | 
      ||
| 151 | $validates = $model->{$association}->validateAssociated($values, $options);  | 
      ||
| 152 |                                         } else {
 | 
      ||
| 153 | $model->{$association}->create(null);  | 
      ||
| 154 | $validates = $model->{$association}->set($values) && $model->{$association}->validates($options);  | 
      ||
| 155 | $data[$association] = $model->{$association}->data[$model->{$association}->alias];  | 
      ||
| 156 | }  | 
      ||
| 157 | if (is_array($validates)) {  | 
      ||
| 158 | $validates = !in_array(false, Hash::flatten($validates), true);  | 
      ||
| 159 | }  | 
      ||
| 160 | $return[$association] = $validates;  | 
      ||
| 161 | } elseif ($associations[$association] === 'hasMany') {  | 
      ||
| 162 | $validates = $model->{$association}->validateMany($values, $options);  | 
      ||
| 163 | $return[$association] = $validates;  | 
      ||
| 164 | }  | 
      ||
| 165 | if (!$validates || (is_array($validates) && in_array(false, $validates, true))) {  | 
      ||
| 166 | $validationErrors[$association] = $model->{$association}->validationErrors;  | 
      ||
| 167 | }  | 
      ||
| 168 | }  | 
      ||
| 169 | }  | 
      ||
| 170 | |||
| 171 | $model->validationErrors = $validationErrors;  | 
      ||
| 172 | if (isset($validationErrors[$model->alias])) {  | 
      ||
| 173 | $model->validationErrors = $validationErrors[$model->alias];  | 
      ||
| 174 | unset($validationErrors[$model->alias]);  | 
      ||
| 175 | $model->validationErrors = array_merge($model->validationErrors, $validationErrors);  | 
      ||
| 176 | }  | 
      ||
| 177 | if (!$options['atomic']) {  | 
      ||
| 178 | return $return;  | 
      ||
| 179 | }  | 
      ||
| 180 | if ($return[$model->alias] === false || !empty($model->validationErrors)) {  | 
      ||
| 181 | return false;  | 
      ||
| 182 | }  | 
      ||
| 183 | return true;  | 
      ||
| 184 | }  | 
      ||
| 185 | |||
| 186 | /**
 | 
      ||
| 187 |  * Validates multiple individual records for a single model
 | 
      ||
| 188 |  *
 | 
      ||
| 189 |  * #### Options
 | 
      ||
| 190 |  *
 | 
      ||
| 191 |  * - atomic: If true (default), returns boolean. If false returns array.
 | 
      ||
| 192 |  * - fieldList: Equivalent to the $fieldList parameter in Model::save()
 | 
      ||
| 193 |  * - deep: If set to true, all associated data will be validated as well.
 | 
      ||
| 194 |  *
 | 
      ||
| 195 |  * Warning: This method could potentially change the passed argument `$data`,
 | 
      ||
| 196 |  * If you do not want this to happen, make a copy of `$data` before passing it
 | 
      ||
| 197 |  * to this method
 | 
      ||
| 198 |  *
 | 
      ||
| 199 |  * @param array &$data Record data to validate. This should be a numerically-indexed array
 | 
      ||
| 200 |  * @param array $options Options to use when validating record data (see above), See also $options of validates().
 | 
      ||
| 201 |  * @return mixed If atomic: True on success, or false on failure.
 | 
      ||
| 202 |  *    Otherwise: array similar to the $data array passed, but values are set to true/false
 | 
      ||
| 203 |  *    depending on whether each record validated successfully.
 | 
      ||
| 204 |  */
 | 
      ||
| 205 | public function validateMany(&$data, $options = array()) {  | 
      ||
| 206 | $model = $this->getModel();  | 
      ||
| 207 | $options += array('atomic' => true, 'deep' => false);  | 
      ||
| 208 | $model->validationErrors = $validationErrors = $return = array();  | 
      ||
| 209 | foreach ($data as $key => &$record) {  | 
      ||
| 210 | if ($options['deep']) {  | 
      ||
| 211 | $validates = $model->validateAssociated($record, $options);  | 
      ||
| 212 |                         } else {
 | 
      ||
| 213 | $model->create(null);  | 
      ||
| 214 | $validates = $model->set($record) && $model->validates($options);  | 
      ||
| 215 | $data[$key] = $model->data;  | 
      ||
| 216 | }  | 
      ||
| 217 | if ($validates === false || (is_array($validates) && in_array(false, Hash::flatten($validates), true))) {  | 
      ||
| 218 | $validationErrors[$key] = $model->validationErrors;  | 
      ||
| 219 | $validates = false;  | 
      ||
| 220 |                         } else {
 | 
      ||
| 221 | $validates = true;  | 
      ||
| 222 | }  | 
      ||
| 223 | $return[$key] = $validates;  | 
      ||
| 224 | }  | 
      ||
| 225 | $model->validationErrors = $validationErrors;  | 
      ||
| 226 | if (!$options['atomic']) {  | 
      ||
| 227 | return $return;  | 
      ||
| 228 | }  | 
      ||
| 229 | return empty($model->validationErrors);  | 
      ||
| 230 | }  | 
      ||
| 231 | |||
| 232 | /**
 | 
      ||
| 233 |  * Returns an array of fields that have failed validation. On the current model. This method will
 | 
      ||
| 234 |  * actually run validation rules over data, not just return the messages.
 | 
      ||
| 235 |  *
 | 
      ||
| 236 |  * @param string $options An optional array of custom options to be made available in the beforeValidate callback
 | 
      ||
| 237 |  * @return array Array of invalid fields
 | 
      ||
| 238 |  * @triggers Model.afterValidate $model
 | 
      ||
| 239 |  * @see ModelValidator::validates()
 | 
      ||
| 240 |  */
 | 
      ||
| 241 | public function errors($options = array()) {  | 
      ||
| 242 | if (!$this->_triggerBeforeValidate($options)) {  | 
      ||
| 243 | return false;  | 
      ||
| 244 | }  | 
      ||
| 245 | $model = $this->getModel();  | 
      ||
| 246 | |||
| 247 | if (!$this->_parseRules()) {  | 
      ||
| 248 | return $model->validationErrors;  | 
      ||
| 249 | }  | 
      ||
| 250 | |||
| 251 | $fieldList = $model->whitelist;  | 
      ||
| 252 | if (empty($fieldList) && !empty($options['fieldList'])) {  | 
      ||
| 253 | if (!empty($options['fieldList'][$model->alias]) && is_array($options['fieldList'][$model->alias])) {  | 
      ||
| 254 | $fieldList = $options['fieldList'][$model->alias];  | 
      ||
| 255 |                         } else {
 | 
      ||
| 256 | $fieldList = $options['fieldList'];  | 
      ||
| 257 | }  | 
      ||
| 258 | }  | 
      ||
| 259 | |||
| 260 | $exists = $model->exists();  | 
      ||
| 261 | $methods = $this->getMethods();  | 
      ||
| 262 | $fields = $this->_validationList($fieldList);  | 
      ||
| 263 | |||
| 264 | foreach ($fields as $field) {  | 
      ||
| 265 | $field->setMethods($methods);  | 
      ||
| 266 | $field->setValidationDomain($model->validationDomain);  | 
      ||
| 267 | $data = isset($model->data[$model->alias]) ? $model->data[$model->alias] : array();  | 
      ||
| 268 | $errors = $field->validate($data, $exists);  | 
      ||
| 269 | foreach ($errors as $error) {  | 
      ||
| 270 | $this->invalidate($field->field, $error);  | 
      ||
| 271 | }  | 
      ||
| 272 | }  | 
      ||
| 273 | |||
| 274 | $model->getEventManager()->dispatch(new CakeEvent('Model.afterValidate', $model));  | 
      ||
| 275 | return $model->validationErrors;  | 
      ||
| 276 | }  | 
      ||
| 277 | |||
| 278 | /**
 | 
      ||
| 279 |  * Marks a field as invalid, optionally setting a message explaining
 | 
      ||
| 280 |  * why the rule failed
 | 
      ||
| 281 |  *
 | 
      ||
| 282 |  * @param string $field The name of the field to invalidate
 | 
      ||
| 283 |  * @param string $message Validation message explaining why the rule failed, defaults to true.
 | 
      ||
| 284 |  * @return void
 | 
      ||
| 285 |  */
 | 
      ||
| 286 | public function invalidate($field, $message = true) {  | 
      ||
| 287 | $this->getModel()->validationErrors[$field][] = $message;  | 
      ||
| 288 | }  | 
      ||
| 289 | |||
| 290 | /**
 | 
      ||
| 291 |  * Gets all possible custom methods from the Model and attached Behaviors
 | 
      ||
| 292 |  * to be used as validators
 | 
      ||
| 293 |  *
 | 
      ||
| 294 |  * @return array List of callables to be used as validation methods
 | 
      ||
| 295 |  */
 | 
      ||
| 296 | public function getMethods() {  | 
      ||
| 297 | $behaviors = $this->_model->Behaviors->enabled();  | 
      ||
| 298 | if (!empty($this->_methods) && $behaviors === $this->_behaviors) {  | 
      ||
| 299 | return $this->_methods;  | 
      ||
| 300 | }  | 
      ||
| 301 | $this->_behaviors = $behaviors;  | 
      ||
| 302 | |||
| 303 | if (empty($this->_modelMethods)) {  | 
      ||
| 304 | foreach (get_class_methods($this->_model) as $method) {  | 
      ||
| 305 | $this->_modelMethods[strtolower($method)] = array($this->_model, $method);  | 
      ||
| 306 | }  | 
      ||
| 307 | }  | 
      ||
| 308 | |||
| 309 | $methods = $this->_modelMethods;  | 
      ||
| 310 | foreach (array_keys($this->_model->Behaviors->methods()) as $method) {  | 
      ||
| 311 | $methods += array(strtolower($method) => array($this->_model, $method));  | 
      ||
| 312 | }  | 
      ||
| 313 | |||
| 314 | return $this->_methods = $methods;  | 
      ||
| 315 | }  | 
      ||
| 316 | |||
| 317 | /**
 | 
      ||
| 318 |  * Returns a CakeValidationSet object containing all validation rules for a field, if no
 | 
      ||
| 319 |  * params are passed then it returns an array with all CakeValidationSet objects for each field
 | 
      ||
| 320 |  *
 | 
      ||
| 321 |  * @param string $name [optional] The fieldname to fetch. Defaults to null.
 | 
      ||
| 322 |  * @return CakeValidationSet|array|null
 | 
      ||
| 323 |  */
 | 
      ||
| 324 | public function getField($name = null) {  | 
      ||
| 325 |                 $this->_parseRules();
 | 
      ||
| 326 | if ($name !== null) {  | 
      ||
| 327 | if (!empty($this->_fields[$name])) {  | 
      ||
| 328 | return $this->_fields[$name];  | 
      ||
| 329 | }  | 
      ||
| 330 | return null;  | 
      ||
| 331 | }  | 
      ||
| 332 | return $this->_fields;  | 
      ||
| 333 | }  | 
      ||
| 334 | |||
| 335 | /**
 | 
      ||
| 336 |  * Sets the CakeValidationSet objects from the `Model::$validate` property
 | 
      ||
| 337 |  * If `Model::$validate` is not set or empty, this method returns false. True otherwise.
 | 
      ||
| 338 |  *
 | 
      ||
| 339 |  * @return bool true if `Model::$validate` was processed, false otherwise
 | 
      ||
| 340 |  */
 | 
      ||
| 341 | protected function _parseRules() {  | 
      ||
| 342 | if ($this->_validate === $this->_model->validate) {  | 
      ||
| 343 | return true;  | 
      ||
| 344 | }  | 
      ||
| 345 | |||
| 346 | if (empty($this->_model->validate)) {  | 
      ||
| 347 | $this->_validate = array();  | 
      ||
| 348 | $this->_fields = array();  | 
      ||
| 349 | return false;  | 
      ||
| 350 | }  | 
      ||
| 351 | |||
| 352 | $this->_validate = $this->_model->validate;  | 
      ||
| 353 | $this->_fields = array();  | 
      ||
| 354 | $methods = $this->getMethods();  | 
      ||
| 355 | foreach ($this->_validate as $fieldName => $ruleSet) {  | 
      ||
| 356 | $this->_fields[$fieldName] = new CakeValidationSet($fieldName, $ruleSet);  | 
      ||
| 357 | $this->_fields[$fieldName]->setMethods($methods);  | 
      ||
| 358 | }  | 
      ||
| 359 | return true;  | 
      ||
| 360 | }  | 
      ||
| 361 | |||
| 362 | /**
 | 
      ||
| 363 |  * Sets the I18n domain for validation messages. This method is chainable.
 | 
      ||
| 364 |  *
 | 
      ||
| 365 |  * @param string $validationDomain [optional] The validation domain to be used.
 | 
      ||
| 366 |  * @return $this
 | 
      ||
| 367 |  */
 | 
      ||
| 368 | public function setValidationDomain($validationDomain = null) {  | 
      ||
| 369 | if (empty($validationDomain)) {  | 
      ||
| 370 | $validationDomain = 'default';  | 
      ||
| 371 | }  | 
      ||
| 372 | $this->getModel()->validationDomain = $validationDomain;  | 
      ||
| 373 | return $this;  | 
      ||
| 374 | }  | 
      ||
| 375 | |||
| 376 | /**
 | 
      ||
| 377 |  * Gets the model related to this validator
 | 
      ||
| 378 |  *
 | 
      ||
| 379 |  * @return Model
 | 
      ||
| 380 |  */
 | 
      ||
| 381 | public function getModel() {  | 
      ||
| 382 | return $this->_model;  | 
      ||
| 383 | }  | 
      ||
| 384 | |||
| 385 | /**
 | 
      ||
| 386 |  * Processes the passed fieldList and returns the list of fields to be validated
 | 
      ||
| 387 |  *
 | 
      ||
| 388 |  * @param array $fieldList list of fields to be used for validation
 | 
      ||
| 389 |  * @return array List of validation rules to be applied
 | 
      ||
| 390 |  */
 | 
      ||
| 391 | protected function _validationList($fieldList = array()) {  | 
      ||
| 392 | if (empty($fieldList) || Hash::dimensions($fieldList) > 1) {  | 
      ||
| 393 | return $this->_fields;  | 
      ||
| 394 | }  | 
      ||
| 395 | |||
| 396 | $validateList = array();  | 
      ||
| 397 | $this->validationErrors = array();  | 
      ||
| 398 | foreach ((array)$fieldList as $f) {  | 
      ||
| 399 | if (!empty($this->_fields[$f])) {  | 
      ||
| 400 | $validateList[$f] = $this->_fields[$f];  | 
      ||
| 401 | }  | 
      ||
| 402 | }  | 
      ||
| 403 | |||
| 404 | return $validateList;  | 
      ||
| 405 | }  | 
      ||
| 406 | |||
| 407 | /**
 | 
      ||
| 408 |  * Runs validation for hasAndBelongsToMany associations that have 'with' keys
 | 
      ||
| 409 |  * set and data in the data set.
 | 
      ||
| 410 |  *
 | 
      ||
| 411 |  * @param array $options Array of options to use on Validation of with models
 | 
      ||
| 412 |  * @return bool Failure of validation on with models.
 | 
      ||
| 413 |  * @see Model::validates()
 | 
      ||
| 414 |  */
 | 
      ||
| 415 | protected function _validateWithModels($options) {  | 
      ||
| 416 | $valid = true;  | 
      ||
| 417 | $model = $this->getModel();  | 
      ||
| 418 | |||
| 419 | foreach ($model->hasAndBelongsToMany as $assoc => $association) {  | 
      ||
| 420 | if (empty($association['with']) || !isset($model->data[$assoc])) {  | 
      ||
| 421 |                                 continue;
 | 
      ||
| 422 | }  | 
      ||
| 423 | list($join) = $model->joinModel($model->hasAndBelongsToMany[$assoc]['with']);  | 
      ||
| 424 | $data = $model->data[$assoc];  | 
      ||
| 425 | |||
| 426 | $newData = array();  | 
      ||
| 427 | foreach ((array)$data as $row) {  | 
      ||
| 428 | if (isset($row[$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {  | 
      ||
| 429 | $newData[] = $row;  | 
      ||
| 430 | } elseif (isset($row[$join]) && isset($row[$join][$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {  | 
      ||
| 431 | $newData[] = $row[$join];  | 
      ||
| 432 | }  | 
      ||
| 433 | }  | 
      ||
| 434 | foreach ($newData as $data) {  | 
      ||
| 435 | $data[$model->hasAndBelongsToMany[$assoc]['foreignKey']] = $model->id;  | 
      ||
| 436 | $model->{$join}->create($data);  | 
      ||
| 437 | $valid = ($valid && $model->{$join}->validator()->validates($options));  | 
      ||
| 438 | }  | 
      ||
| 439 | }  | 
      ||
| 440 | return $valid;  | 
      ||
| 441 | }  | 
      ||
| 442 | |||
| 443 | /**
 | 
      ||
| 444 |  * Propagates beforeValidate event
 | 
      ||
| 445 |  *
 | 
      ||
| 446 |  * @param array $options Options to pass to callback.
 | 
      ||
| 447 |  * @return bool
 | 
      ||
| 448 |  * @triggers Model.beforeValidate $model, array($options)
 | 
      ||
| 449 |  */
 | 
      ||
| 450 | protected function _triggerBeforeValidate($options = array()) {  | 
      ||
| 451 | $model = $this->getModel();  | 
      ||
| 452 | $event = new CakeEvent('Model.beforeValidate', $model, array($options));  | 
      ||
| 453 | list($event->break, $event->breakOn) = array(true, false);  | 
      ||
| 454 | $model->getEventManager()->dispatch($event);  | 
      ||
| 455 | if ($event->isStopped()) {  | 
      ||
| 456 | return false;  | 
      ||
| 457 | }  | 
      ||
| 458 | return true;  | 
      ||
| 459 | }  | 
      ||
| 460 | |||
| 461 | /**
 | 
      ||
| 462 |  * Returns whether a rule set is defined for a field or not
 | 
      ||
| 463 |  *
 | 
      ||
| 464 |  * @param string $field name of the field to check
 | 
      ||
| 465 |  * @return bool
 | 
      ||
| 466 |  */
 | 
      ||
| 467 | public function offsetExists($field) {  | 
      ||
| 468 |                 $this->_parseRules();
 | 
      ||
| 469 | return isset($this->_fields[$field]);  | 
      ||
| 470 | }  | 
      ||
| 471 | |||
| 472 | /**
 | 
      ||
| 473 |  * Returns the rule set for a field
 | 
      ||
| 474 |  *
 | 
      ||
| 475 |  * @param string $field name of the field to check
 | 
      ||
| 476 |  * @return CakeValidationSet
 | 
      ||
| 477 |  */
 | 
      ||
| 478 | public function offsetGet($field) {  | 
      ||
| 479 |                 $this->_parseRules();
 | 
      ||
| 480 | return $this->_fields[$field];  | 
      ||
| 481 | }  | 
      ||
| 482 | |||
| 483 | /**
 | 
      ||
| 484 |  * Sets the rule set for a field
 | 
      ||
| 485 |  *
 | 
      ||
| 486 |  * @param string $field name of the field to set
 | 
      ||
| 487 |  * @param array|CakeValidationSet $rules set of rules to apply to field
 | 
      ||
| 488 |  * @return void
 | 
      ||
| 489 |  */
 | 
      ||
| 490 | public function offsetSet($field, $rules) {  | 
      ||
| 491 |                 $this->_parseRules();
 | 
      ||
| 492 | if (!$rules instanceof CakeValidationSet) {  | 
      ||
| 493 | $rules = new CakeValidationSet($field, $rules);  | 
      ||
| 494 | $methods = $this->getMethods();  | 
      ||
| 495 | $rules->setMethods($methods);  | 
      ||
| 496 | }  | 
      ||
| 497 | $this->_fields[$field] = $rules;  | 
      ||
| 498 | }  | 
      ||
| 499 | |||
| 500 | /**
 | 
      ||
| 501 |  * Unsets the rule set for a field
 | 
      ||
| 502 |  *
 | 
      ||
| 503 |  * @param string $field name of the field to unset
 | 
      ||
| 504 |  * @return void
 | 
      ||
| 505 |  */
 | 
      ||
| 506 | public function offsetUnset($field) {  | 
      ||
| 507 |                 $this->_parseRules();
 | 
      ||
| 508 | unset($this->_fields[$field]);  | 
      ||
| 509 | }  | 
      ||
| 510 | |||
| 511 | /**
 | 
      ||
| 512 |  * Returns an iterator for each of the fields to be validated
 | 
      ||
| 513 |  *
 | 
      ||
| 514 |  * @return ArrayIterator
 | 
      ||
| 515 |  */
 | 
      ||
| 516 | public function getIterator() {  | 
      ||
| 517 |                 $this->_parseRules();
 | 
      ||
| 518 | return new ArrayIterator($this->_fields);  | 
      ||
| 519 | }  | 
      ||
| 520 | |||
| 521 | /**
 | 
      ||
| 522 |  * Returns the number of fields having validation rules
 | 
      ||
| 523 |  *
 | 
      ||
| 524 |  * @return int
 | 
      ||
| 525 |  */
 | 
      ||
| 526 | public function count() {  | 
      ||
| 527 |                 $this->_parseRules();
 | 
      ||
| 528 | return count($this->_fields);  | 
      ||
| 529 | }  | 
      ||
| 530 | |||
| 531 | /**
 | 
      ||
| 532 |  * Adds a new rule to a field's rule set. If second argument is an array or instance of
 | 
      ||
| 533 |  * CakeValidationSet then rules list for the field will be replaced with second argument and
 | 
      ||
| 534 |  * third argument will be ignored.
 | 
      ||
| 535 |  *
 | 
      ||
| 536 |  * ## Example:
 | 
      ||
| 537 |  *
 | 
      ||
| 538 |  * ```
 | 
      ||
| 539 |  *                $validator
 | 
      ||
| 540 |  *                        ->add('title', 'required', array('rule' => 'notBlank', 'required' => true))
 | 
      ||
| 541 |  *                        ->add('user_id', 'valid', array('rule' => 'numeric', 'message' => 'Invalid User'))
 | 
      ||
| 542 |  *
 | 
      ||
| 543 |  *                $validator->add('password', array(
 | 
      ||
| 544 |  *                        'size' => array('rule' => array('lengthBetween', 8, 20)),
 | 
      ||
| 545 |  *                        'hasSpecialCharacter' => array('rule' => 'validateSpecialchar', 'message' => 'not valid')
 | 
      ||
| 546 |  *                ));
 | 
      ||
| 547 |  * ```
 | 
      ||
| 548 |  *
 | 
      ||
| 549 |  * @param string $field The name of the field where the rule is to be added
 | 
      ||
| 550 |  * @param string|array|CakeValidationSet $name name of the rule to be added or list of rules for the field
 | 
      ||
| 551 |  * @param array|CakeValidationRule $rule or list of rules to be added to the field's rule set
 | 
      ||
| 552 |  * @return $this
 | 
      ||
| 553 |  */
 | 
      ||
| 554 | public function add($field, $name, $rule = null) {  | 
      ||
| 555 |                 $this->_parseRules();
 | 
      ||
| 556 | if ($name instanceof CakeValidationSet) {  | 
      ||
| 557 | $this->_fields[$field] = $name;  | 
      ||
| 558 | return $this;  | 
      ||
| 559 | }  | 
      ||
| 560 | |||
| 561 | if (!isset($this->_fields[$field])) {  | 
      ||
| 562 | $rule = (is_string($name)) ? array($name => $rule) : $name;  | 
      ||
| 563 | $this->_fields[$field] = new CakeValidationSet($field, $rule);  | 
      ||
| 564 |                 } else {
 | 
      ||
| 565 | if (is_string($name)) {  | 
      ||
| 566 | $this->_fields[$field]->setRule($name, $rule);  | 
      ||
| 567 |                         } else {
 | 
      ||
| 568 | $this->_fields[$field]->setRules($name);  | 
      ||
| 569 | }  | 
      ||
| 570 | }  | 
      ||
| 571 | |||
| 572 | $methods = $this->getMethods();  | 
      ||
| 573 | $this->_fields[$field]->setMethods($methods);  | 
      ||
| 574 | |||
| 575 | return $this;  | 
      ||
| 576 | }  | 
      ||
| 577 | |||
| 578 | /**
 | 
      ||
| 579 |  * Removes a rule from the set by its name
 | 
      ||
| 580 |  *
 | 
      ||
| 581 |  * ## Example:
 | 
      ||
| 582 |  *
 | 
      ||
| 583 |  * ```
 | 
      ||
| 584 |  *                $validator
 | 
      ||
| 585 |  *                        ->remove('title', 'required')
 | 
      ||
| 586 |  *                        ->remove('user_id')
 | 
      ||
| 587 |  * ```
 | 
      ||
| 588 |  *
 | 
      ||
| 589 |  * @param string $field The name of the field from which the rule will be removed
 | 
      ||
| 590 |  * @param string $rule the name of the rule to be removed
 | 
      ||
| 591 |  * @return $this
 | 
      ||
| 592 |  */
 | 
      ||
| 593 | public function remove($field, $rule = null) {  | 
      ||
| 594 |                 $this->_parseRules();
 | 
      ||
| 595 | if ($rule === null) {  | 
      ||
| 596 | unset($this->_fields[$field]);  | 
      ||
| 597 |                 } else {
 | 
      ||
| 598 | $this->_fields[$field]->removeRule($rule);  | 
      ||
| 599 | }  | 
      ||
| 600 | return $this;  | 
      ||
| 601 | }  | 
      ||
| 602 | }  |