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.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 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:  * PHP versions 5
  8:  *
  9:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 10:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 11:  *
 12:  * Licensed under The MIT License
 13:  * Redistributions of files must retain the above copyright notice.
 14:  *
 15:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 16:  * @link          http://cakephp.org CakePHP(tm) Project
 17:  * @package       Cake.Model
 18:  * @since         CakePHP(tm) v 2.2.0
 19:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 20:  */
 21: 
 22: App::uses('CakeValidationSet', 'Model/Validator');
 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 wheter 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:         if (!($model->set($data) && $model->validates($options))) {
134:             $validationErrors[$model->alias] = $model->validationErrors;
135:             $return[$model->alias] = false;
136:         } else {
137:             $return[$model->alias] = true;
138:         }
139:         $data = $model->data;
140:         if (!empty($options['deep']) && isset($data[$model->alias])) {
141:             $recordData = $data[$model->alias];
142:             unset($data[$model->alias]);
143:             $data = array_merge($data, $recordData);
144:         }
145: 
146:         $associations = $model->getAssociated();
147:         foreach ($data as $association => &$values) {
148:             $validates = true;
149:             if (isset($associations[$association])) {
150:                 if (in_array($associations[$association], array('belongsTo', 'hasOne'))) {
151:                     if ($options['deep']) {
152:                         $validates = $model->{$association}->validateAssociated($values, $options);
153:                     } else {
154:                         $model->{$association}->create(null);
155:                         $validates = $model->{$association}->set($values) && $model->{$association}->validates($options);
156:                         $data[$association] = $model->{$association}->data[$model->{$association}->alias];
157:                     }
158:                     if (is_array($validates)) {
159:                         if (in_array(false, Hash::flatten($validates), true)) {
160:                             $validates = false;
161:                         } else {
162:                             $validates = true;
163:                         }
164:                     }
165:                     $return[$association] = $validates;
166:                 } elseif ($associations[$association] === 'hasMany') {
167:                     $validates = $model->{$association}->validateMany($values, $options);
168:                     $return[$association] = $validates;
169:                 }
170:                 if (!$validates || (is_array($validates) && in_array(false, $validates, true))) {
171:                     $validationErrors[$association] = $model->{$association}->validationErrors;
172:                 }
173:             }
174:         }
175: 
176:         $model->validationErrors = $validationErrors;
177:         if (isset($validationErrors[$model->alias])) {
178:             $model->validationErrors = $validationErrors[$model->alias];
179:             unset($validationErrors[$model->alias]);
180:             $model->validationErrors = array_merge($model->validationErrors, $validationErrors);
181:         }
182:         if (!$options['atomic']) {
183:             return $return;
184:         }
185:         if ($return[$model->alias] === false || !empty($model->validationErrors)) {
186:             return false;
187:         }
188:         return true;
189:     }
190: 
191: /**
192:  * Validates multiple individual records for a single model
193:  *
194:  * #### Options
195:  *
196:  * - atomic: If true (default), returns boolean. If false returns array.
197:  * - fieldList: Equivalent to the $fieldList parameter in Model::save()
198:  * - deep: If set to true, all associated data will be validated as well.
199:  *
200:  * Warning: This method could potentially change the passed argument `$data`,
201:  * If you do not want this to happen, make a copy of `$data` before passing it
202:  * to this method
203:  *
204:  * @param array $data Record data to validate. This should be a numerically-indexed array
205:  * @param array $options Options to use when validating record data (see above), See also $options of validates().
206:  * @return boolean True on success, or false on failure.
207:  * @return mixed If atomic: True on success, or false on failure.
208:  *    Otherwise: array similar to the $data array passed, but values are set to true/false
209:  *    depending on whether each record validated successfully.
210:  */
211:     public function validateMany(&$data, $options = array()) {
212:         $model = $this->getModel();
213:         $options = array_merge(array('atomic' => true, 'deep' => false), $options);
214:         $model->validationErrors = $validationErrors = $return = array();
215:         foreach ($data as $key => &$record) {
216:             if ($options['deep']) {
217:                 $validates = $model->validateAssociated($record, $options);
218:             } else {
219:                 $model->create(null);
220:                 $validates = $model->set($record) && $model->validates($options);
221:                 $data[$key] = $model->data;
222:             }
223:             if ($validates === false || (is_array($validates) && in_array(false, Hash::flatten($validates), true))) {
224:                 $validationErrors[$key] = $model->validationErrors;
225:                 $validates = false;
226:             } else {
227:                 $validates = true;
228:             }
229:             $return[$key] = $validates;
230:         }
231:         $model->validationErrors = $validationErrors;
232:         if (!$options['atomic']) {
233:             return $return;
234:         }
235:         if (empty($model->validationErrors)) {
236:             return true;
237:         }
238:         return false;
239:     }
240: 
241: /**
242:  * Returns an array of fields that have failed validation. On the current model. This method will
243:  * actually run validation rules over data, not just return the messages.
244:  *
245:  * @param string $options An optional array of custom options to be made available in the beforeValidate callback
246:  * @return array Array of invalid fields
247:  * @see ModelValidator::validates()
248:  */
249:     public function errors($options = array()) {
250:         if (!$this->_triggerBeforeValidate($options)) {
251:             return false;
252:         }
253:         $model = $this->getModel();
254: 
255:         if (!$this->_parseRules()) {
256:             return $model->validationErrors;
257:         }
258: 
259:         $fieldList = isset($options['fieldList']) ? $options['fieldList'] : array();
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
323:  */
324:     public function getField($name = null) {
325:         $this->_parseRules();
326:         if ($name !== null && !empty($this->_fields[$name])) {
327:             return $this->_fields[$name];
328:         } elseif ($name !== null) {
329:             return null;
330:         }
331:         return $this->_fields;
332:     }
333: 
334: /**
335:  * Sets the CakeValidationSet objects from the `Model::$validate` property
336:  * If `Model::$validate` is not set or empty, this method returns false. True otherwise.
337:  *
338:  * @return boolean true if `Model::$validate` was processed, false otherwise
339:  */
340:     protected function _parseRules() {
341:         if ($this->_validate === $this->_model->validate) {
342:             return true;
343:         }
344: 
345:         if (empty($this->_model->validate)) {
346:             $this->_validate = array();
347:             $this->_fields = array();
348:             return false;
349:         }
350: 
351:         $this->_validate = $this->_model->validate;
352:         $this->_fields = array();
353:         $methods = $this->getMethods();
354:         foreach ($this->_validate as $fieldName => $ruleSet) {
355:             $this->_fields[$fieldName] = new CakeValidationSet($fieldName, $ruleSet);
356:             $this->_fields[$fieldName]->setMethods($methods);
357:         }
358:         return true;
359:     }
360: 
361: /**
362:  * Sets the I18n domain for validation messages. This method is chainable.
363:  *
364:  * @param string $validationDomain [optional] The validation domain to be used.
365:  * @return ModelValidator
366:  */
367:     public function setValidationDomain($validationDomain = null) {
368:         if (empty($validationDomain)) {
369:             $validationDomain = 'default';
370:         }
371:         $this->getModel()->validationDomain = $validationDomain;
372:         return $this;
373:     }
374: 
375: /**
376:  * Gets the model related to this validator
377:  *
378:  * @return Model
379:  */
380:     public function getModel() {
381:         return $this->_model;
382:     }
383: 
384: /**
385:  * Processes the Model's whitelist or passed fieldList and returns the list of fields
386:  * 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:         $model = $this->getModel();
393:         $whitelist = $model->whitelist;
394: 
395:         if (!empty($fieldList)) {
396:             if (!empty($fieldList[$model->alias]) && is_array($fieldList[$model->alias])) {
397:                 $whitelist = $fieldList[$model->alias];
398:             } else {
399:                 $whitelist = $fieldList;
400:             }
401:         }
402:         unset($fieldList);
403: 
404:         $validateList = array();
405:         if (!empty($whitelist)) {
406:             $this->validationErrors = array();
407: 
408:             foreach ((array)$whitelist as $f) {
409:                 if (!empty($this->_fields[$f])) {
410:                     $validateList[$f] = $this->_fields[$f];
411:                 }
412:             }
413:         } else {
414:             return $this->_fields;
415:         }
416: 
417:         return $validateList;
418:     }
419: 
420: /**
421:  * Runs validation for hasAndBelongsToMany associations that have 'with' keys
422:  * set and data in the data set.
423:  *
424:  * @param array $options Array of options to use on Validation of with models
425:  * @return boolean Failure of validation on with models.
426:  * @see Model::validates()
427:  */
428:     protected function _validateWithModels($options) {
429:         $valid = true;
430:         $model = $this->getModel();
431: 
432:         foreach ($model->hasAndBelongsToMany as $assoc => $association) {
433:             if (empty($association['with']) || !isset($model->data[$assoc])) {
434:                 continue;
435:             }
436:             list($join) = $model->joinModel($model->hasAndBelongsToMany[$assoc]['with']);
437:             $data = $model->data[$assoc];
438: 
439:             $newData = array();
440:             foreach ((array)$data as $row) {
441:                 if (isset($row[$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
442:                     $newData[] = $row;
443:                 } elseif (isset($row[$join]) && isset($row[$join][$model->hasAndBelongsToMany[$assoc]['associationForeignKey']])) {
444:                     $newData[] = $row[$join];
445:                 }
446:             }
447:             if (empty($newData)) {
448:                 continue;
449:             }
450:             foreach ($newData as $data) {
451:                 $data[$model->hasAndBelongsToMany[$assoc]['foreignKey']] = $model->id;
452:                 $model->{$join}->create($data);
453:                 $valid = ($valid && $model->{$join}->validator()->validates($options));
454:             }
455:         }
456:         return $valid;
457:     }
458: 
459: /**
460:  * Propagates beforeValidate event
461:  *
462:  * @param array $options
463:  * @return boolean
464:  */
465:     protected function _triggerBeforeValidate($options = array()) {
466:         $model = $this->getModel();
467:         $event = new CakeEvent('Model.beforeValidate', $model, array($options));
468:         list($event->break, $event->breakOn) = array(true, false);
469:         $model->getEventManager()->dispatch($event);
470:         if ($event->isStopped()) {
471:             return false;
472:         }
473:         return true;
474:     }
475: 
476: /**
477:  * Returns wheter a rule set is defined for a field or not
478:  *
479:  * @param string $field name of the field to check
480:  * @return boolean
481:  **/
482:     public function offsetExists($field) {
483:         $this->_parseRules();
484:         return isset($this->_fields[$field]);
485:     }
486: 
487: /**
488:  * Returns the rule set for a field
489:  *
490:  * @param string $field name of the field to check
491:  * @return CakeValidationSet
492:  **/
493:     public function offsetGet($field) {
494:         $this->_parseRules();
495:         return $this->_fields[$field];
496:     }
497: 
498: /**
499:  * Sets the rule set for a field
500:  *
501:  * @param string $field name of the field to set
502:  * @param array|CakeValidationSet $rules set of rules to apply to field
503:  * @return void
504:  **/
505:     public function offsetSet($field, $rules) {
506:         $this->_parseRules();
507:         if (!$rules instanceof CakeValidationSet) {
508:             $rules = new CakeValidationSet($field, $rules);
509:             $methods = $this->getMethods();
510:             $rules->setMethods($methods);
511:         }
512:         $this->_fields[$field] = $rules;
513:     }
514: 
515: /**
516:  * Unsets the rulset for a field
517:  *
518:  * @param string $field name of the field to unset
519:  * @return void
520:  **/
521:     public function offsetUnset($field) {
522:         $this->_parseRules();
523:         unset($this->_fields[$field]);
524:     }
525: 
526: /**
527:  * Returns an iterator for each of the fields to be validated
528:  *
529:  * @return ArrayIterator
530:  **/
531:     public function getIterator() {
532:         $this->_parseRules();
533:         return new ArrayIterator($this->_fields);
534:     }
535: 
536: /**
537:  * Returns the number of fields having validation rules
538:  *
539:  * @return int
540:  **/
541:     public function count() {
542:         $this->_parseRules();
543:         return count($this->_fields);
544:     }
545: 
546: /**
547:  * Adds a new rule to a field's rule set. If second argumet is an array or instance of
548:  * CakeValidationSet then rules list for the field will be replaced with second argument and
549:  * third argument will be ignored.
550:  *
551:  * ## Example:
552:  *
553:  * {{{
554:  *      $validator
555:  *          ->add('title', 'required', array('rule' => 'notEmpty', 'required' => true))
556:  *          ->add('user_id', 'valid', array('rule' => 'numeric', 'message' => 'Invalid User'))
557:  *
558:  *      $validator->add('password', array(
559:  *          'size' => array('rule' => array('between', 8, 20)),
560:  *          'hasSpecialCharacter' => array('rule' => 'validateSpecialchar', 'message' => 'not valid')
561:  *      ));
562:  * }}}
563:  *
564:  * @param string $field The name of the field from wich the rule will be removed
565:  * @param string|array|CakeValidationSet $name name of the rule to be added or list of rules for the field
566:  * @param array|CakeValidationRule $rule or list of rules to be added to the field's rule set
567:  * @return ModelValidator this instance
568:  **/
569:     public function add($field, $name, $rule = null) {
570:         $this->_parseRules();
571:         if ($name instanceof CakeValidationSet) {
572:             $this->_fields[$field] = $name;
573:             return $this;
574:         }
575: 
576:         if (!isset($this->_fields[$field])) {
577:             $rule = (is_string($name)) ? array($name => $rule) : $name;
578:             $this->_fields[$field] = new CakeValidationSet($field, $rule);
579:         } else {
580:             if (is_string($name)) {
581:                 $this->_fields[$field]->setRule($name, $rule);
582:             } else {
583:                 $this->_fields[$field]->setRules($name);
584:             }
585:         }
586: 
587:         $methods = $this->getMethods();
588:         $this->_fields[$field]->setMethods($methods);
589: 
590:         return $this;
591:     }
592: 
593: /**
594:  * Removes a rule from the set by its name
595:  *
596:  * ## Example:
597:  *
598:  * {{{
599:  *      $validator
600:  *          ->remove('title', 'required')
601:  *          ->remove('user_id')
602:  * }}}
603:  *
604:  * @param string $field The name of the field from wich the rule will be removed
605:  * @param string $rule the name of the rule to be removed
606:  * @return ModelValidator this instance
607:  **/
608:     public function remove($field, $rule = null) {
609:         $this->_parseRules();
610:         if ($rule === null) {
611:             unset($this->_fields[$field]);
612:         } else {
613:             $this->_fields[$field]->removeRule($rule);
614:         }
615:         return $this;
616:     }
617: }
618: 
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