CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.4 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.4
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AclNode
  • Aco
  • AcoAction
  • Aro
  • BehaviorCollection
  • CakeSchema
  • ConnectionManager
  • I18nModel
  • Model
  • ModelBehavior
  • ModelValidator
  • Permission
  1: <?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 boolean 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|boolean 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_merge(array('atomic' => true, 'deep' => false), $options);
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_merge(array('atomic' => true, 'deep' => false), $options);
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:  * @see ModelValidator::validates()
239:  */
240:     public function errors($options = array()) {
241:         if (!$this->_triggerBeforeValidate($options)) {
242:             return false;
243:         }
244:         $model = $this->getModel();
245: 
246:         if (!$this->_parseRules()) {
247:             return $model->validationErrors;
248:         }
249: 
250:         $fieldList = isset($options['fieldList']) ? $options['fieldList'] : array();
251:         $exists = $model->exists();
252:         $methods = $this->getMethods();
253:         $fields = $this->_validationList($fieldList);
254: 
255:         foreach ($fields as $field) {
256:             $field->setMethods($methods);
257:             $field->setValidationDomain($model->validationDomain);
258:             $data = isset($model->data[$model->alias]) ? $model->data[$model->alias] : array();
259:             $errors = $field->validate($data, $exists);
260:             foreach ($errors as $error) {
261:                 $this->invalidate($field->field, $error);
262:             }
263:         }
264: 
265:         $model->getEventManager()->dispatch(new CakeEvent('Model.afterValidate', $model));
266:         return $model->validationErrors;
267:     }
268: 
269: /**
270:  * Marks a field as invalid, optionally setting a message explaining
271:  * why the rule failed
272:  *
273:  * @param string $field The name of the field to invalidate
274:  * @param string $message Validation message explaining why the rule failed, defaults to true.
275:  * @return void
276:  */
277:     public function invalidate($field, $message = true) {
278:         $this->getModel()->validationErrors[$field][] = $message;
279:     }
280: 
281: /**
282:  * Gets all possible custom methods from the Model and attached Behaviors
283:  * to be used as validators
284:  *
285:  * @return array List of callables to be used as validation methods
286:  */
287:     public function getMethods() {
288:         $behaviors = $this->_model->Behaviors->enabled();
289:         if (!empty($this->_methods) && $behaviors === $this->_behaviors) {
290:             return $this->_methods;
291:         }
292:         $this->_behaviors = $behaviors;
293: 
294:         if (empty($this->_modelMethods)) {
295:             foreach (get_class_methods($this->_model) as $method) {
296:                 $this->_modelMethods[strtolower($method)] = array($this->_model, $method);
297:             }
298:         }
299: 
300:         $methods = $this->_modelMethods;
301:         foreach (array_keys($this->_model->Behaviors->methods()) as $method) {
302:             $methods += array(strtolower($method) => array($this->_model, $method));
303:         }
304: 
305:         return $this->_methods = $methods;
306:     }
307: 
308: /**
309:  * Returns a CakeValidationSet object containing all validation rules for a field, if no
310:  * params are passed then it returns an array with all CakeValidationSet objects for each field
311:  *
312:  * @param string $name [optional] The fieldname to fetch. Defaults to null.
313:  * @return CakeValidationSet|array
314:  */
315:     public function getField($name = null) {
316:         $this->_parseRules();
317:         if ($name !== null) {
318:             if (!empty($this->_fields[$name])) {
319:                 return $this->_fields[$name];
320:             }
321:             return null;
322:         }
323:         return $this->_fields;
324:     }
325: 
326: /**
327:  * Sets the CakeValidationSet objects from the `Model::$validate` property
328:  * If `Model::$validate` is not set or empty, this method returns false. True otherwise.
329:  *
330:  * @return boolean true if `Model::$validate` was processed, false otherwise
331:  */
332:     protected function _parseRules() {
333:         if ($this->_validate === $this->_model->validate) {
334:             return true;
335:         }
336: 
337:         if (empty($this->_model->validate)) {
338:             $this->_validate = array();
339:             $this->_fields = array();
340:             return false;
341:         }
342: 
343:         $this->_validate = $this->_model->validate;
344:         $this->_fields = array();
345:         $methods = $this->getMethods();
346:         foreach ($this->_validate as $fieldName => $ruleSet) {
347:             $this->_fields[$fieldName] = new CakeValidationSet($fieldName, $ruleSet);
348:             $this->_fields[$fieldName]->setMethods($methods);
349:         }
350:         return true;
351:     }
352: 
353: /**
354:  * Sets the I18n domain for validation messages. This method is chainable.
355:  *
356:  * @param string $validationDomain [optional] The validation domain to be used.
357:  * @return ModelValidator
358:  */
359:     public function setValidationDomain($validationDomain = null) {
360:         if (empty($validationDomain)) {
361:             $validationDomain = 'default';
362:         }
363:         $this->getModel()->validationDomain = $validationDomain;
364:         return $this;
365:     }
366: 
367: /**
368:  * Gets the model related to this validator
369:  *
370:  * @return Model
371:  */
372:     public function getModel() {
373:         return $this->_model;
374:     }
375: 
376: /**
377:  * Processes the Model's whitelist or passed fieldList and returns the list of fields
378:  * to be validated
379:  *
380:  * @param array $fieldList list of fields to be used for validation
381:  * @return array List of validation rules to be applied
382:  */
383:     protected function _validationList($fieldList = array()) {
384:         $model = $this->getModel();
385:         $whitelist = $model->whitelist;
386: 
387:         if (!empty($fieldList)) {
388:             if (!empty($fieldList[$model->alias]) && is_array($fieldList[$model->alias])) {
389:                 $whitelist = $fieldList[$model->alias];
390:             } else {
391:                 $whitelist = $fieldList;
392:             }
393:         }
394:         unset($fieldList);
395: 
396:         if (empty($whitelist) || Hash::dimensions($whitelist) > 1) {
397:             return $this->_fields;
398:         }
399: 
400:         $validateList = array();
401:         $this->validationErrors = array();
402:         foreach ((array)$whitelist as $f) {
403:             if (!empty($this->_fields[$f])) {
404:                 $validateList[$f] = $this->_fields[$f];
405:             }
406:         }
407: 
408:         return $validateList;
409:     }
410: 
411: /**
412:  * Runs validation for hasAndBelongsToMany associations that have 'with' keys
413:  * set and data in the data set.
414:  *
415:  * @param array $options Array of options to use on Validation of with models
416:  * @return boolean Failure of validation on with models.
417:  * @see Model::validates()
418:  */
419:     protected function _validateWithModels($options) {
420:         $valid = true;
421:         $model = $this->getModel();
422: 
423:         foreach ($model->hasAndBelongsToMany as $assoc => $association) {
424:             if (empty($association['with']) || !isset($model->data[$assoc])) {
425:                 continue;
426:             }
427:             list($join) = $model->joinModel($model->hasAndBelongsToMany[$assoc]['with']);
428:             $data = $model->data[$assoc];
429: 
430:             $newData = array();
431:             foreach ((array)$data as $row) {
432:                 if (isset($row[$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
433:                     $newData[] = $row;
434:                 } elseif (isset($row[$join]) && isset($row[$join][$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
435:                     $newData[] = $row[$join];
436:                 }
437:             }
438:             foreach ($newData as $data) {
439:                 $data[$model->hasAndBelongsToMany[$assoc]['foreignKey']] = $model->id;
440:                 $model->{$join}->create($data);
441:                 $valid = ($valid && $model->{$join}->validator()->validates($options));
442:             }
443:         }
444:         return $valid;
445:     }
446: 
447: /**
448:  * Propagates beforeValidate event
449:  *
450:  * @param array $options
451:  * @return boolean
452:  */
453:     protected function _triggerBeforeValidate($options = array()) {
454:         $model = $this->getModel();
455:         $event = new CakeEvent('Model.beforeValidate', $model, array($options));
456:         list($event->break, $event->breakOn) = array(true, false);
457:         $model->getEventManager()->dispatch($event);
458:         if ($event->isStopped()) {
459:             return false;
460:         }
461:         return true;
462:     }
463: 
464: /**
465:  * Returns whether a rule set is defined for a field or not
466:  *
467:  * @param string $field name of the field to check
468:  * @return boolean
469:  */
470:     public function offsetExists($field) {
471:         $this->_parseRules();
472:         return isset($this->_fields[$field]);
473:     }
474: 
475: /**
476:  * Returns the rule set for a field
477:  *
478:  * @param string $field name of the field to check
479:  * @return CakeValidationSet
480:  */
481:     public function offsetGet($field) {
482:         $this->_parseRules();
483:         return $this->_fields[$field];
484:     }
485: 
486: /**
487:  * Sets the rule set for a field
488:  *
489:  * @param string $field name of the field to set
490:  * @param array|CakeValidationSet $rules set of rules to apply to field
491:  * @return void
492:  */
493:     public function offsetSet($field, $rules) {
494:         $this->_parseRules();
495:         if (!$rules instanceof CakeValidationSet) {
496:             $rules = new CakeValidationSet($field, $rules);
497:             $methods = $this->getMethods();
498:             $rules->setMethods($methods);
499:         }
500:         $this->_fields[$field] = $rules;
501:     }
502: 
503: /**
504:  * Unsets the rule set for a field
505:  *
506:  * @param string $field name of the field to unset
507:  * @return void
508:  */
509:     public function offsetUnset($field) {
510:         $this->_parseRules();
511:         unset($this->_fields[$field]);
512:     }
513: 
514: /**
515:  * Returns an iterator for each of the fields to be validated
516:  *
517:  * @return ArrayIterator
518:  */
519:     public function getIterator() {
520:         $this->_parseRules();
521:         return new ArrayIterator($this->_fields);
522:     }
523: 
524: /**
525:  * Returns the number of fields having validation rules
526:  *
527:  * @return integer
528:  */
529:     public function count() {
530:         $this->_parseRules();
531:         return count($this->_fields);
532:     }
533: 
534: /**
535:  * Adds a new rule to a field's rule set. If second argument is an array or instance of
536:  * CakeValidationSet then rules list for the field will be replaced with second argument and
537:  * third argument will be ignored.
538:  *
539:  * ## Example:
540:  *
541:  * {{{
542:  *      $validator
543:  *          ->add('title', 'required', array('rule' => 'notEmpty', 'required' => true))
544:  *          ->add('user_id', 'valid', array('rule' => 'numeric', 'message' => 'Invalid User'))
545:  *
546:  *      $validator->add('password', array(
547:  *          'size' => array('rule' => array('between', 8, 20)),
548:  *          'hasSpecialCharacter' => array('rule' => 'validateSpecialchar', 'message' => 'not valid')
549:  *      ));
550:  * }}}
551:  *
552:  * @param string $field The name of the field from which the rule will be removed
553:  * @param string|array|CakeValidationSet $name name of the rule to be added or list of rules for the field
554:  * @param array|CakeValidationRule $rule or list of rules to be added to the field's rule set
555:  * @return ModelValidator this instance
556:  */
557:     public function add($field, $name, $rule = null) {
558:         $this->_parseRules();
559:         if ($name instanceof CakeValidationSet) {
560:             $this->_fields[$field] = $name;
561:             return $this;
562:         }
563: 
564:         if (!isset($this->_fields[$field])) {
565:             $rule = (is_string($name)) ? array($name => $rule) : $name;
566:             $this->_fields[$field] = new CakeValidationSet($field, $rule);
567:         } else {
568:             if (is_string($name)) {
569:                 $this->_fields[$field]->setRule($name, $rule);
570:             } else {
571:                 $this->_fields[$field]->setRules($name);
572:             }
573:         }
574: 
575:         $methods = $this->getMethods();
576:         $this->_fields[$field]->setMethods($methods);
577: 
578:         return $this;
579:     }
580: 
581: /**
582:  * Removes a rule from the set by its name
583:  *
584:  * ## Example:
585:  *
586:  * {{{
587:  *      $validator
588:  *          ->remove('title', 'required')
589:  *          ->remove('user_id')
590:  * }}}
591:  *
592:  * @param string $field The name of the field from which the rule will be removed
593:  * @param string $rule the name of the rule to be removed
594:  * @return ModelValidator this instance
595:  */
596:     public function remove($field, $rule = null) {
597:         $this->_parseRules();
598:         if ($rule === null) {
599:             unset($this->_fields[$field]);
600:         } else {
601:             $this->_fields[$field]->removeRule($rule);
602:         }
603:         return $this;
604:     }
605: }
606: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs