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

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.6
      • 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 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' => 'notEmpty', '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: }
603: 
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