Completed
Push — fix-numbervalidator-comma-deci... ( 08054b...a7f0a3 )
by Alexander
40:41 queued 37:41
created

CompareValidator::validateAttribute()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.5125
c 0
b 0
f 0
ccs 19
cts 19
cp 1
cc 5
eloc 16
nc 7
nop 2
crap 5
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
8
namespace yii\validators;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
use yii\helpers\Html;
13
14
/**
15
 * CompareValidator compares the specified attribute value with another value.
16
 *
17
 * The value being compared with can be another attribute value
18
 * (specified via [[compareAttribute]]) or a constant (specified via
19
 * [[compareValue]]. When both are specified, the latter takes
20
 * precedence. If neither is specified, the attribute will be compared
21
 * with another attribute whose name is by appending "_repeat" to the source
22
 * attribute name.
23
 *
24
 * CompareValidator supports different comparison operators, specified
25
 * via the [[operator]] property.
26
 *
27
 * The default comparison function is based on string values, which means the values
28
 * are compared byte by byte. When comparing numbers, make sure to set the [[$type]]
29
 * to [[TYPE_NUMBER]] to enable numeric comparison.
30
 *
31
 * @author Qiang Xue <[email protected]>
32
 * @since 2.0
33
 */
34
class CompareValidator extends Validator
35
{
36
    /**
37
     * Constant for specifying the comparison [[type]] by numeric values.
38
     * @since 2.0.11
39
     * @see type
40
     */
41
    const TYPE_STRING = 'string';
42
    /**
43
     * Constant for specifying the comparison [[type]] by numeric values.
44
     * @since 2.0.11
45
     * @see type
46
     */
47
    const TYPE_NUMBER = 'number';
48
49
    /**
50
     * @var string the name of the attribute to be compared with. When both this property
51
     * and [[compareValue]] are set, the latter takes precedence. If neither is set,
52
     * it assumes the comparison is against another attribute whose name is formed by
53
     * appending '_repeat' to the attribute being validated. For example, if 'password' is
54
     * being validated, then the attribute to be compared would be 'password_repeat'.
55
     * @see compareValue
56
     */
57
    public $compareAttribute;
58
    /**
59
     * @var mixed the constant value to be compared with. When both this property
60
     * and [[compareAttribute]] are set, this property takes precedence.
61
     * @see compareAttribute
62
     */
63
    public $compareValue;
64
    /**
65
     * @var string the type of the values being compared. The follow types are supported:
66
     *
67
     * - [[TYPE_STRING|string]]: the values are being compared as strings. No conversion will be done before comparison.
68
     * - [[TYPE_NUMBER|number]]: the values are being compared as numbers. String values will be converted into numbers before comparison.
69
     */
70
    public $type = self::TYPE_STRING;
71
    /**
72
     * @var string the operator for comparison. The following operators are supported:
73
     *
74
     * - `==`: check if two values are equal. The comparison is done is non-strict mode.
75
     * - `===`: check if two values are equal. The comparison is done is strict mode.
76
     * - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.
77
     * - `!==`: check if two values are NOT equal. The comparison is done is strict mode.
78
     * - `>`: check if value being validated is greater than the value being compared with.
79
     * - `>=`: check if value being validated is greater than or equal to the value being compared with.
80
     * - `<`: check if value being validated is less than the value being compared with.
81
     * - `<=`: check if value being validated is less than or equal to the value being compared with.
82
     *
83
     * When you want to compare numbers, make sure to also set [[type]] to `number`.
84
     */
85
    public $operator = '==';
86
    /**
87
     * @var string the user-defined error message. It may contain the following placeholders which
88
     * will be replaced accordingly by the validator:
89
     *
90
     * - `{attribute}`: the label of the attribute being validated
91
     * - `{value}`: the value of the attribute being validated
92
     * - `{compareValue}`: the value or the attribute label to be compared with
93
     * - `{compareAttribute}`: the label of the attribute to be compared with
94
     * - `{compareValueOrAttribute}`: the value or the attribute label to be compared with
95
     */
96
    public $message;
97
98
99
    /**
100
     * @inheritdoc
101
     */
102 7
    public function init()
103
    {
104 7
        parent::init();
105 7
        if ($this->message === null) {
106 7
            switch ($this->operator) {
107 7
                case '==':
108 5
                    $this->message = Yii::t('yii', '{attribute} must be equal to "{compareValueOrAttribute}".');
109 5
                    break;
110 4
                case '===':
111 3
                    $this->message = Yii::t('yii', '{attribute} must be equal to "{compareValueOrAttribute}".');
112 3
                    break;
113 4
                case '!=':
114 4
                    $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValueOrAttribute}".');
115 4
                    break;
116 3
                case '!==':
117 3
                    $this->message = Yii::t('yii', '{attribute} must not be equal to "{compareValueOrAttribute}".');
118 3
                    break;
119 3
                case '>':
120 3
                    $this->message = Yii::t('yii', '{attribute} must be greater than "{compareValueOrAttribute}".');
121 3
                    break;
122 3
                case '>=':
123 3
                    $this->message = Yii::t('yii', '{attribute} must be greater than or equal to "{compareValueOrAttribute}".');
124 3
                    break;
125 3
                case '<':
126 3
                    $this->message = Yii::t('yii', '{attribute} must be less than "{compareValueOrAttribute}".');
127 3
                    break;
128 3
                case '<=':
129 3
                    $this->message = Yii::t('yii', '{attribute} must be less than or equal to "{compareValueOrAttribute}".');
130 3
                    break;
131 1
                default:
132 1
                    throw new InvalidConfigException("Unknown operator: {$this->operator}");
133 7
            }
134 7
        }
135 7
    }
136
137
    /**
138
     * @inheritdoc
139
     */
140 4
    public function validateAttribute($model, $attribute)
141
    {
142 4
        $value = $model->$attribute;
143 4
        if (is_array($value)) {
144 1
            $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
145
146 1
            return;
147
        }
148 4
        if ($this->compareValue !== null) {
149 3
            $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;
150 3
        } else {
151 3
            $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
152 3
            $compareValue = $model->$compareAttribute;
153 3
            $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);
154
        }
155
156 4
        if (!$this->compareValues($this->operator, $this->type, $value, $compareValue)) {
157 4
            $this->addError($model, $attribute, $this->message, [
158 4
                'compareAttribute' => $compareLabel,
159 4
                'compareValue' => $compareValue,
160 4
                'compareValueOrAttribute' => $compareValueOrAttribute,
161 4
            ]);
162 4
        }
163 4
    }
164
165
    /**
166
     * @inheritdoc
167
     */
168 2
    protected function validateValue($value)
169
    {
170 2
        if ($this->compareValue === null) {
171 1
            throw new InvalidConfigException('CompareValidator::compareValue must be set.');
172
        }
173 1
        if (!$this->compareValues($this->operator, $this->type, $value, $this->compareValue)) {
174 1
            return [$this->message, [
175 1
                'compareAttribute' => $this->compareValue,
176 1
                'compareValue' => $this->compareValue,
177 1
                'compareValueOrAttribute' => $this->compareValue,
178 1
            ]];
179
        } else {
180 1
            return null;
181
        }
182
    }
183
184
    /**
185
     * Compares two values with the specified operator.
186
     * @param string $operator the comparison operator
187
     * @param string $type the type of the values being compared
188
     * @param mixed $value the value being compared
189
     * @param mixed $compareValue another value being compared
190
     * @return bool whether the comparison using the specified operator is true.
191
     */
192 5
    protected function compareValues($operator, $type, $value, $compareValue)
193
    {
194 5
        if ($type === self::TYPE_NUMBER) {
195
            $value = (float) $value;
196
            $compareValue = (float) $compareValue;
197
        } else {
198 5
            $value = (string) $value;
199 5
            $compareValue = (string) $compareValue;
200
        }
201
        switch ($operator) {
202 5
            case '==':
203 4
                return $value == $compareValue;
204 5
            case '===':
205 3
                return $value === $compareValue;
206 5
            case '!=':
207 4
                return $value != $compareValue;
208 4
            case '!==':
209 3
                return $value !== $compareValue;
210 4
            case '>':
211 3
                return $value > $compareValue;
212 4
            case '>=':
213 3
                return $value >= $compareValue;
214 4
            case '<':
215 3
                return $value < $compareValue;
216 4
            case '<=':
217 3
                return $value <= $compareValue;
218 1
            default:
219 1
                return false;
220 1
        }
221
    }
222
223
    /**
224
     * @inheritdoc
225
     */
226
    public function clientValidateAttribute($model, $attribute, $view)
227
    {
228
        ValidationAsset::register($view);
229
        $options = $this->getClientOptions($model, $attribute);
230
231
        return 'yii.validation.compare(value, messages, ' . json_encode($options, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ');';
232
    }
233
234
    /**
235
     * @inheritdoc
236
     */
237
    public function getClientOptions($model, $attribute)
238
    {
239
        $options = [
240
            'operator' => $this->operator,
241
            'type' => $this->type,
242
        ];
243
244
        if ($this->compareValue !== null) {
245
            $options['compareValue'] = $this->compareValue;
246
            $compareLabel = $compareValue = $compareValueOrAttribute = $this->compareValue;
247
        } else {
248
            $compareAttribute = $this->compareAttribute === null ? $attribute . '_repeat' : $this->compareAttribute;
249
            $compareValue = $model->getAttributeLabel($compareAttribute);
250
            $options['compareAttribute'] = Html::getInputId($model, $compareAttribute);
251
            $compareLabel = $compareValueOrAttribute = $model->getAttributeLabel($compareAttribute);
252
        }
253
254
        if ($this->skipOnEmpty) {
255
            $options['skipOnEmpty'] = 1;
256
        }
257
258
        $options['message'] = Yii::$app->getI18n()->format($this->message, [
259
            'attribute' => $model->getAttributeLabel($attribute),
260
            'compareAttribute' => $compareLabel,
261
            'compareValue' => $compareValue,
262
            'compareValueOrAttribute' => $compareValueOrAttribute,
263
        ], Yii::$app->language);
264
265
        return $options;
266
    }
267
}
268