1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.0.3
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Validation;
16:
17: use Cake\Event\EventDispatcherInterface;
18: use RuntimeException;
19:
20: /**
21: * A trait that provides methods for building and
22: * interacting with Validators.
23: *
24: * This trait is useful when building ORM like features where
25: * the implementing class wants to build and customize a variety
26: * of validator instances.
27: *
28: * This trait expects that classes including it define three constants:
29: *
30: * - `DEFAULT_VALIDATOR` - The default validator name.
31: * - `VALIDATOR_PROVIDER_NAME ` - The provider name the including class is assigned
32: * in validators.
33: * - `BUILD_VALIDATOR_EVENT` - The name of the event to be triggred when validators
34: * are built.
35: *
36: * If the including class also implements events the `Model.buildValidator` event
37: * will be triggered when validators are created.
38: */
39: trait ValidatorAwareTrait
40: {
41: /**
42: * Validator class.
43: *
44: * @var string
45: */
46: protected $_validatorClass = Validator::class;
47:
48: /**
49: * A list of validation objects indexed by name
50: *
51: * @var \Cake\Validation\Validator[]
52: */
53: protected $_validators = [];
54:
55: /**
56: * Returns the validation rules tagged with $name. It is possible to have
57: * multiple different named validation sets, this is useful when you need
58: * to use varying rules when saving from different routines in your system.
59: *
60: * There are two different ways of creating and naming validation sets: by
61: * creating a new method inside your own Table subclass, or by building
62: * the validator object yourself and storing it using this method.
63: *
64: * For example, if you wish to create a validation set called 'forSubscription',
65: * you will need to create a method in your Table subclass as follows:
66: *
67: * ```
68: * public function validationForSubscription($validator)
69: * {
70: * return $validator
71: * ->add('email', 'valid-email', ['rule' => 'email'])
72: * ->add('password', 'valid', ['rule' => 'notBlank'])
73: * ->requirePresence('username');
74: * }
75: * ```
76: *
77: * Otherwise, you can build the object by yourself and store it in the Table object:
78: *
79: * ```
80: * $validator = new \Cake\Validation\Validator($table);
81: * $validator
82: * ->add('email', 'valid-email', ['rule' => 'email'])
83: * ->add('password', 'valid', ['rule' => 'notBlank'])
84: * ->allowEmpty('bio');
85: * $table->setValidator('forSubscription', $validator);
86: * ```
87: *
88: * You can implement the method in `validationDefault` in your Table subclass
89: * should you wish to have a validation set that applies in cases where no other
90: * set is specified.
91: *
92: * @param string|null $name the name of the validation set to return
93: * @param \Cake\Validation\Validator|null $validator The validator instance to store,
94: * use null to get a validator.
95: * @return \Cake\Validation\Validator
96: * @throws \RuntimeException
97: * @deprecated 3.5.0 Use getValidator/setValidator instead.
98: */
99: public function validator($name = null, Validator $validator = null)
100: {
101: deprecationWarning(
102: 'ValidatorAwareTrait::validator() is deprecated. ' .
103: 'Use ValidatorAwareTrait::getValidator()/setValidator() instead.'
104: );
105: if ($validator !== null) {
106: $name = $name ?: self::DEFAULT_VALIDATOR;
107: $this->setValidator($name, $validator);
108: }
109:
110: return $this->getValidator($name);
111: }
112:
113: /**
114: * Returns the validation rules tagged with $name. It is possible to have
115: * multiple different named validation sets, this is useful when you need
116: * to use varying rules when saving from different routines in your system.
117: *
118: * If a validator has not been set earlier, this method will build a valiator
119: * using a method inside your class.
120: *
121: * For example, if you wish to create a validation set called 'forSubscription',
122: * you will need to create a method in your Table subclass as follows:
123: *
124: * ```
125: * public function validationForSubscription($validator)
126: * {
127: * return $validator
128: * ->add('email', 'valid-email', ['rule' => 'email'])
129: * ->add('password', 'valid', ['rule' => 'notBlank'])
130: * ->requirePresence('username');
131: * }
132: * $validator = $this->getValidator('forSubscription');
133: * ```
134: *
135: * You can implement the method in `validationDefault` in your Table subclass
136: * should you wish to have a validation set that applies in cases where no other
137: * set is specified.
138: *
139: * If a $name argument has not been provided, the default validator will be returned.
140: * You can configure your default validator name in a `DEFAULT_VALIDATOR`
141: * class constant.
142: *
143: * @param string|null $name The name of the validation set to return.
144: * @return \Cake\Validation\Validator
145: */
146: public function getValidator($name = null)
147: {
148: $name = $name ?: self::DEFAULT_VALIDATOR;
149: if (!isset($this->_validators[$name])) {
150: $validator = $this->createValidator($name);
151: $this->setValidator($name, $validator);
152: }
153:
154: return $this->_validators[$name];
155: }
156:
157: /**
158: * Creates a validator using a custom method inside your class.
159: *
160: * This method is used only to build a new validator and it does not store
161: * it in your object. If you want to build and reuse validators,
162: * use getValidator() method instead.
163: *
164: * @param string $name The name of the validation set to create.
165: * @return \Cake\Validation\Validator
166: * @throws \RuntimeException
167: */
168: protected function createValidator($name)
169: {
170: $method = 'validation' . ucfirst($name);
171: if (!$this->validationMethodExists($method)) {
172: $message = sprintf('The %s::%s() validation method does not exists.', __CLASS__, $method);
173: throw new RuntimeException($message);
174: }
175:
176: $validator = new $this->_validatorClass();
177: $validator = $this->$method($validator);
178: if ($this instanceof EventDispatcherInterface) {
179: $event = defined(self::class . '::BUILD_VALIDATOR_EVENT') ? self::BUILD_VALIDATOR_EVENT : 'Model.buildValidator';
180: $this->dispatchEvent($event, compact('validator', 'name'));
181: }
182:
183: if (!$validator instanceof Validator) {
184: throw new RuntimeException(sprintf('The %s::%s() validation method must return an instance of %s.', __CLASS__, $method, Validator::class));
185: }
186:
187: return $validator;
188: }
189:
190: /**
191: * This method stores a custom validator under the given name.
192: *
193: * You can build the object by yourself and store it in your object:
194: *
195: * ```
196: * $validator = new \Cake\Validation\Validator($table);
197: * $validator
198: * ->add('email', 'valid-email', ['rule' => 'email'])
199: * ->add('password', 'valid', ['rule' => 'notBlank'])
200: * ->allowEmpty('bio');
201: * $this->setValidator('forSubscription', $validator);
202: * ```
203: *
204: * @param string $name The name of a validator to be set.
205: * @param \Cake\Validation\Validator $validator Validator object to be set.
206: * @return $this
207: */
208: public function setValidator($name, Validator $validator)
209: {
210: $validator->setProvider(self::VALIDATOR_PROVIDER_NAME, $this);
211: $this->_validators[$name] = $validator;
212:
213: return $this;
214: }
215:
216: /**
217: * Checks whether or not a validator has been set.
218: *
219: * @param string $name The name of a validator.
220: * @return bool
221: */
222: public function hasValidator($name)
223: {
224: $method = 'validation' . ucfirst($name);
225: if ($this->validationMethodExists($method)) {
226: return true;
227: }
228:
229: return isset($this->_validators[$name]);
230: }
231:
232: /**
233: * Checks if validation method exists.
234: *
235: * @param string $name Validation method name.
236: * @return bool
237: */
238: protected function validationMethodExists($name)
239: {
240: return method_exists($this, $name);
241: }
242:
243: /**
244: * Returns the default validator object. Subclasses can override this function
245: * to add a default validation set to the validator object.
246: *
247: * @param \Cake\Validation\Validator $validator The validator that can be modified to
248: * add some rules to it.
249: * @return \Cake\Validation\Validator
250: */
251: public function validationDefault(Validator $validator)
252: {
253: return $validator;
254: }
255: }
256: