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: /**
43: * Validator class.
44: *
45: * @var string
46: */
47: protected $_validatorClass = Validator::class;
48:
49: /**
50: * A list of validation objects indexed by name
51: *
52: * @var \Cake\Validation\Validator[]
53: */
54: protected $_validators = [];
55:
56: /**
57: * Returns the validation rules tagged with $name. It is possible to have
58: * multiple different named validation sets, this is useful when you need
59: * to use varying rules when saving from different routines in your system.
60: *
61: * There are two different ways of creating and naming validation sets: by
62: * creating a new method inside your own Table subclass, or by building
63: * the validator object yourself and storing it using this method.
64: *
65: * For example, if you wish to create a validation set called 'forSubscription',
66: * you will need to create a method in your Table subclass as follows:
67: *
68: * ```
69: * public function validationForSubscription($validator)
70: * {
71: * return $validator
72: * ->add('email', 'valid-email', ['rule' => 'email'])
73: * ->add('password', 'valid', ['rule' => 'notBlank'])
74: * ->requirePresence('username');
75: * }
76: * ```
77: *
78: * Otherwise, you can build the object by yourself and store it in the Table object:
79: *
80: * ```
81: * $validator = new \Cake\Validation\Validator($table);
82: * $validator
83: * ->add('email', 'valid-email', ['rule' => 'email'])
84: * ->add('password', 'valid', ['rule' => 'notBlank'])
85: * ->allowEmpty('bio');
86: * $table->setValidator('forSubscription', $validator);
87: * ```
88: *
89: * You can implement the method in `validationDefault` in your Table subclass
90: * should you wish to have a validation set that applies in cases where no other
91: * set is specified.
92: *
93: * @param string|null $name the name of the validation set to return
94: * @param \Cake\Validation\Validator|null $validator The validator instance to store,
95: * use null to get a validator.
96: * @return \Cake\Validation\Validator
97: * @throws \RuntimeException
98: * @deprecated 3.5.0 Use getValidator/setValidator instead.
99: */
100: public function validator($name = null, Validator $validator = null)
101: {
102: deprecationWarning(
103: 'ValidatorAwareTrait::validator() is deprecated. ' .
104: 'Use ValidatorAwareTrait::getValidator()/setValidator() instead.'
105: );
106: if ($validator !== null) {
107: $name = $name ?: self::DEFAULT_VALIDATOR;
108: $this->setValidator($name, $validator);
109: }
110:
111: return $this->getValidator($name);
112: }
113:
114: /**
115: * Returns the validation rules tagged with $name. It is possible to have
116: * multiple different named validation sets, this is useful when you need
117: * to use varying rules when saving from different routines in your system.
118: *
119: * If a validator has not been set earlier, this method will build a valiator
120: * using a method inside your class.
121: *
122: * For example, if you wish to create a validation set called 'forSubscription',
123: * you will need to create a method in your Table subclass as follows:
124: *
125: * ```
126: * public function validationForSubscription($validator)
127: * {
128: * return $validator
129: * ->add('email', 'valid-email', ['rule' => 'email'])
130: * ->add('password', 'valid', ['rule' => 'notBlank'])
131: * ->requirePresence('username');
132: * }
133: * $validator = $this->getValidator('forSubscription');
134: * ```
135: *
136: * You can implement the method in `validationDefault` in your Table subclass
137: * should you wish to have a validation set that applies in cases where no other
138: * set is specified.
139: *
140: * If a $name argument has not been provided, the default validator will be returned.
141: * You can configure your default validator name in a `DEFAULT_VALIDATOR`
142: * class constant.
143: *
144: * @param string|null $name The name of the validation set to return.
145: * @return \Cake\Validation\Validator
146: */
147: public function getValidator($name = null)
148: {
149: $name = $name ?: self::DEFAULT_VALIDATOR;
150: if (!isset($this->_validators[$name])) {
151: $validator = $this->createValidator($name);
152: $this->setValidator($name, $validator);
153: }
154:
155: return $this->_validators[$name];
156: }
157:
158: /**
159: * Creates a validator using a custom method inside your class.
160: *
161: * This method is used only to build a new validator and it does not store
162: * it in your object. If you want to build and reuse validators,
163: * use getValidator() method instead.
164: *
165: * @param string $name The name of the validation set to create.
166: * @return \Cake\Validation\Validator
167: * @throws \RuntimeException
168: */
169: protected function createValidator($name)
170: {
171: $method = 'validation' . ucfirst($name);
172: if (!$this->validationMethodExists($method)) {
173: $message = sprintf('The %s::%s() validation method does not exists.', __CLASS__, $method);
174: throw new RuntimeException($message);
175: }
176:
177: $validator = new $this->_validatorClass;
178: $validator = $this->$method($validator);
179: if ($this instanceof EventDispatcherInterface) {
180: $event = defined(self::class . '::BUILD_VALIDATOR_EVENT') ? self::BUILD_VALIDATOR_EVENT : 'Model.buildValidator';
181: $this->dispatchEvent($event, compact('validator', 'name'));
182: }
183:
184: if (!$validator instanceof Validator) {
185: throw new RuntimeException(sprintf('The %s::%s() validation method must return an instance of %s.', __CLASS__, $method, Validator::class));
186: }
187:
188: return $validator;
189: }
190:
191: /**
192: * This method stores a custom validator under the given name.
193: *
194: * You can build the object by yourself and store it in your object:
195: *
196: * ```
197: * $validator = new \Cake\Validation\Validator($table);
198: * $validator
199: * ->add('email', 'valid-email', ['rule' => 'email'])
200: * ->add('password', 'valid', ['rule' => 'notBlank'])
201: * ->allowEmpty('bio');
202: * $this->setValidator('forSubscription', $validator);
203: * ```
204: *
205: * @param string $name The name of a validator to be set.
206: * @param \Cake\Validation\Validator $validator Validator object to be set.
207: * @return $this
208: */
209: public function setValidator($name, Validator $validator)
210: {
211: $validator->setProvider(self::VALIDATOR_PROVIDER_NAME, $this);
212: $this->_validators[$name] = $validator;
213:
214: return $this;
215: }
216:
217: /**
218: * Checks whether or not a validator has been set.
219: *
220: * @param string $name The name of a validator.
221: * @return bool
222: */
223: public function hasValidator($name)
224: {
225: $method = 'validation' . ucfirst($name);
226: if ($this->validationMethodExists($method)) {
227: return true;
228: }
229:
230: return isset($this->_validators[$name]);
231: }
232:
233: /**
234: * Checks if validation method exists.
235: *
236: * @param string $name Validation method name.
237: * @return bool
238: */
239: protected function validationMethodExists($name)
240: {
241: return method_exists($this, $name);
242: }
243:
244: /**
245: * Returns the default validator object. Subclasses can override this function
246: * to add a default validation set to the validator object.
247: *
248: * @param \Cake\Validation\Validator $validator The validator that can be modified to
249: * add some rules to it.
250: * @return \Cake\Validation\Validator
251: */
252: public function validationDefault(Validator $validator)
253: {
254: return $validator;
255: }
256: }
257: