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.

CompareValidator.php 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. * When you want to compare numbers, make sure to also set [[type]] to `number`.
  64. */
  65. public $operator = '==';
  66. /**
  67. * @var string the user-defined error message. It may contain the following placeholders which
  68. * will be replaced accordingly by the validator:
  69. *
  70. * - `{attribute}`: the label of the attribute being validated
  71. * - `{value}`: the value of the attribute being validated
  72. * - `{compareValue}`: the value or the attribute label to be compared with
  73. * - `{compareAttribute}`: the label of the attribute to be compared with
  74. * - `{compareValueOrAttribute}`: the value or the attribute label to be compared with
  75. */
  76. public $message;
  77. /**
  78. * @inheritdoc
  79. */
  80. public function init()
  81. {
  82. parent::init();
  83. if ($this->message === null) {
  84. switch ($this->operator) {
  85. case '==':
  86. $this->message = Yii::t('yii', '{attribute} must be equal to "{compareValueOrAttribute}".');
  87. break;
  88. case '===':
  89. $this->message = Yii::t('yii', '{attribute} must be equal to "{compareValueOrAttribute}".');
  90. break;
  91. case '!=':
  92. $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValueOrAttribute}".');
  93. break;
  94. case '!==':
  95. $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValueOrAttribute}".');
  96. break;
  97. case '>':
  98. $this->message = Yii::t('yii', '{attribute} must be greater than "{compareValueOrAttribute}".');
  99. break;
  100. case '>=':
  101. $this->message = Yii::t('yii', '{attribute} must be greater than or equal to "{compareValueOrAttribute}".');
  102. break;
  103. case '<':
  104. $this->message = Yii::t('yii', '{attribute} must be less than "{compareValueOrAttribute}".');
  105. break;
  106. case '<=':
  107. $this->message = Yii::t('yii', '{attribute} must be less than or equal to "{compareValueOrAttribute}".');
  108. break;
  109. default:
  110. throw new InvalidConfigException("Unknown operator: {$this->operator}");
  111. }
  112. }
  113. }
  114. /**
  115. * @inheritdoc
  116. */
  117. public function validateAttribute($model, $attribute)
  118. {
  119. $value = $model->$attribute;
  120. if (is_array($value)) {
  121. $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
  122. return;
  123. }
  124. if ($this->compareValue !== null) {
  125. $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;
  126. } else {
  127. $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
  128. $compareValue = $model->$compareAttribute;
  129. $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);
  130. }
  131. if (!$this->compareValues($this->operator, $this->type, $value, $compareValue)) {
  132. $this->addError($model, $attribute, $this->message, [
  133. 'compareAttribute' => $compareLabel,
  134. 'compareValue' => $compareValue,
  135. 'compareValueOrAttribute' => $compareValueOrAttribute,
  136. ]);
  137. }
  138. }
  139. /**
  140. * @inheritdoc
  141. */
  142. protected function validateValue($value)
  143. {
  144. if ($this->compareValue === null) {
  145. throw new InvalidConfigException('CompareValidator::compareValue must be set.');
  146. }
  147. if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
  148. return [$this->message, [
  149. 'compareAttribute' => $this->compareValue,
  150. 'compareValue' => $this->compareValue,
  151. 'compareValueOrAttribute' => $this->compareValue,
  152. ]];
  153. } else {
  154. return null;
  155. }
  156. }
  157. /**
  158. * Compares two values with the specified operator.
  159. * @param string $operator the comparison operator
  160. * @param string $type the type of the values being compared
  161. * @param mixed $value the value being compared
  162. * @param mixed $compareValue another value being compared
  163. * @return boolean whether the comparison using the specified operator is true.
  164. */
  165. protected function compareValues($operator, $type, $value, $compareValue)
  166. {
  167. if ($type === 'number') {
  168. $value = (float) $value;
  169. $compareValue = (float) $compareValue;
  170. } else {
  171. $value = (string) $value;
  172. $compareValue = (string) $compareValue;
  173. }
  174. switch ($operator) {
  175. case '==':
  176. return $value == $compareValue;
  177. case '===':
  178. return $value === $compareValue;
  179. case '!=':
  180. return $value != $compareValue;
  181. case '!==':
  182. return $value !== $compareValue;
  183. case '>':
  184. return $value > $compareValue;
  185. case '>=':
  186. return $value >= $compareValue;
  187. case '<':
  188. return $value < $compareValue;
  189. case '<=':
  190. return $value <= $compareValue;
  191. default:
  192. return false;
  193. }
  194. }
  195. /**
  196. * @inheritdoc
  197. */
  198. public function clientValidateAttribute($model, $attribute, $view)
  199. {
  200. $options = [
  201. 'operator' => $this->operator,
  202. 'type' => $this->type,
  203. ];
  204. if ($this->compareValue !== null) {
  205. $options['compareValue'] = $this->compareValue;
  206. $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;
  207. } else {
  208. $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
  209. $compareValue = $model->getAttributeLabel($compareAttribute);
  210. $options['compareAttribute'] = Html::getInputId($model, $compareAttribute);
  211. $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);
  212. }
  213. if ($this->skipOnEmpty) {
  214. $options['skipOnEmpty'] = 1;
  215. }
  216. $options['message'] = Yii::$app->getI18n()->format($this->message, [
  217. 'attribute' => $model->getAttributeLabel($attribute),
  218. 'compareAttribute' => $compareLabel,
  219. 'compareValue' => $compareValue,
  220. 'compareValueOrAttribute' => $compareValueOrAttribute,
  221. ], Yii::$app->language);
  222. ValidationAsset::register($view);
  223. return 'yii.validation.compare(value, messages, ' . json_encode($options, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ');';
  224. }
  225. }