You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
8.9KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\validators;
  8. use Yii;
  9. use yii\base\InvalidConfigException;
  10. use yii\helpers\Html;
  11. /**
  12. * CompareValidator compares the specified attribute value with another value.
  13. *
  14. * The value being compared with can be another attribute value
  15. * (specified via [[compareAttribute]]) or a constant (specified via
  16. * [[compareValue]]. When both are specified, the latter takes
  17. * precedence. If neither is specified, the attribute will be compared
  18. * with another attribute whose name is by appending "_repeat" to the source
  19. * attribute name.
  20. *
  21. * CompareValidator supports different comparison operators, specified
  22. * via the [[operator]] property.
  23. *
  24. * @author Qiang Xue <qiang.xue@gmail.com>
  25. * @since 2.0
  26. */
  27. class CompareValidator extends Validator
  28. {
  29. /**
  30. * @var string the name of the attribute to be compared with. When both this property
  31. * and [[compareValue]] are set, the latter takes precedence. If neither is set,
  32. * it assumes the comparison is against another attribute whose name is formed by
  33. * appending '_repeat' to the attribute being validated. For example, if 'password' is
  34. * being validated, then the attribute to be compared would be 'password_repeat'.
  35. * @see compareValue
  36. */
  37. public $compareAttribute;
  38. /**
  39. * @var mixed the constant value to be compared with. When both this property
  40. * and [[compareAttribute]] are set, this property takes precedence.
  41. * @see compareAttribute
  42. */
  43. public $compareValue;
  44. /**
  45. * @var string the type of the values being compared. The follow types are supported:
  46. *
  47. * - string: the values are being compared as strings. No conversion will be done before comparison.
  48. * - number: the values are being compared as numbers. String values will be converted into numbers before comparison.
  49. */
  50. public $type = 'string';
  51. /**
  52. * @var string the operator for comparison. The following operators are supported:
  53. *
  54. * - `==`: check if two values are equal. The comparison is done is non-strict mode.
  55. * - `===`: check if two values are equal. The comparison is done is strict mode.
  56. * - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.
  57. * - `!==`: check if two values are NOT equal. The comparison is done is strict mode.
  58. * - `>`: check if value being validated is greater than the value being compared with.
  59. * - `>=`: check if value being validated is greater than or equal to the value being compared with.
  60. * - `<`: check if value being validated is less than the value being compared with.
  61. * - `<=`: check if value being validated is less than or equal to the value being compared with.
  62. */
  63. public $operator = '==';
  64. /**
  65. * @var string the user-defined error message. It may contain the following placeholders which
  66. * will be replaced accordingly by the validator:
  67. *
  68. * - `{attribute}`: the label of the attribute being validated
  69. * - `{value}`: the value of the attribute being validated
  70. * - `{compareValue}`: the value or the attribute label to be compared with
  71. * - `{compareAttribute}`: the label of the attribute to be compared with
  72. */
  73. public $message;
  74. /**
  75. * @inheritdoc
  76. */
  77. public function init()
  78. {
  79. parent::init();
  80. if ($this->message === null) {
  81. switch ($this->operator) {
  82. case '==':
  83. $this->message = Yii::t('yii', '{attribute} must be repeated exactly.');
  84. break;
  85. case '===':
  86. $this->message = Yii::t('yii', '{attribute} must be repeated exactly.');
  87. break;
  88. case '!=':
  89. $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValue}".');
  90. break;
  91. case '!==':
  92. $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValue}".');
  93. break;
  94. case '>':
  95. $this->message = Yii::t('yii', '{attribute} must be greater than "{compareValue}".');
  96. break;
  97. case '>=':
  98. $this->message = Yii::t('yii', '{attribute} must be greater than or equal to "{compareValue}".');
  99. break;
  100. case '<':
  101. $this->message = Yii::t('yii', '{attribute} must be less than "{compareValue}".');
  102. break;
  103. case '<=':
  104. $this->message = Yii::t('yii', '{attribute} must be less than or equal to "{compareValue}".');
  105. break;
  106. default:
  107. throw new InvalidConfigException("Unknown operator: {$this->operator}");
  108. }
  109. }
  110. }
  111. /**
  112. * @inheritdoc
  113. */
  114. public function validateAttribute($model, $attribute)
  115. {
  116. $value = $model->$attribute;
  117. if (is_array($value)) {
  118. $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
  119. return;
  120. }
  121. if ($this->compareValue !== null) {
  122. $compareLabel = $compareValue = $this->compareValue;
  123. } else {
  124. $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
  125. $compareValue = $model->$compareAttribute;
  126. $compareLabel = $model->getAttributeLabel($compareAttribute);
  127. }
  128. if (!$this->compareValues($this->operator, $this->type, $value, $compareValue)) {
  129. $this->addError($model, $attribute, $this->message, [
  130. 'compareAttribute' => $compareLabel,
  131. 'compareValue' => $compareValue,
  132. ]);
  133. }
  134. }
  135. /**
  136. * @inheritdoc
  137. */
  138. protected function validateValue($value)
  139. {
  140. if ($this->compareValue === null) {
  141. throw new InvalidConfigException('CompareValidator::compareValue must be set.');
  142. }
  143. if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
  144. return [$this->message, [
  145. 'compareAttribute' => $this->compareValue,
  146. 'compareValue' => $this->compareValue,
  147. ]];
  148. } else {
  149. return null;
  150. }
  151. }
  152. /**
  153. * Compares two values with the specified operator.
  154. * @param string $operator the comparison operator
  155. * @param string $type the type of the values being compared
  156. * @param mixed $value the value being compared
  157. * @param mixed $compareValue another value being compared
  158. * @return boolean whether the comparison using the specified operator is true.
  159. */
  160. protected function compareValues($operator, $type, $value, $compareValue)
  161. {
  162. if ($type === 'number') {
  163. $value = (float) $value;
  164. $compareValue = (float) $compareValue;
  165. } else {
  166. $value = (string) $value;
  167. $compareValue = (string) $compareValue;
  168. }
  169. switch ($operator) {
  170. case '==':
  171. return $value == $compareValue;
  172. case '===':
  173. return $value === $compareValue;
  174. case '!=':
  175. return $value != $compareValue;
  176. case '!==':
  177. return $value !== $compareValue;
  178. case '>':
  179. return $value > $compareValue;
  180. case '>=':
  181. return $value >= $compareValue;
  182. case '<':
  183. return $value < $compareValue;
  184. case '<=':
  185. return $value <= $compareValue;
  186. default:
  187. return false;
  188. }
  189. }
  190. /**
  191. * @inheritdoc
  192. */
  193. public function clientValidateAttribute($model, $attribute, $view)
  194. {
  195. $options = [
  196. 'operator' => $this->operator,
  197. 'type' => $this->type,
  198. ];
  199. if ($this->compareValue !== null) {
  200. $options['compareValue'] = $this->compareValue;
  201. $compareValue = $this->compareValue;
  202. } else {
  203. $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
  204. $compareValue = $model->getAttributeLabel($compareAttribute);
  205. $options['compareAttribute'] = Html::getInputId($model, $compareAttribute);
  206. }
  207. if ($this->skipOnEmpty) {
  208. $options['skipOnEmpty'] = 1;
  209. }
  210. $options['message'] = Yii::$app->getI18n()->format($this->message, [
  211. 'attribute' => $model->getAttributeLabel($attribute),
  212. 'compareAttribute' => $compareValue,
  213. 'compareValue' => $compareValue,
  214. ], Yii::$app->language);
  215. ValidationAsset::register($view);
  216. return 'yii.validation.compare(value, messages, ' . json_encode($options, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ');';
  217. }
  218. }