GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( cd2d3a...6886d6 )
by Robert
09:38
created

UniqueValidator   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 93.15%

Importance

Changes 0
Metric Value
wmc 25
lcom 1
cbo 6
dl 0
loc 162
rs 10
c 0
b 0
f 0
ccs 68
cts 73
cp 0.9315

3 Methods

Rating   Name   Duplication   Size   Complexity  
B init() 0 17 5
F validateAttribute() 0 66 17
A addComboNotUniqueError() 0 18 3
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\db\ActiveRecordInterface;
12
use yii\helpers\Inflector;
13
14
/**
15
 * UniqueValidator validates that the attribute value is unique in the specified database table.
16
 *
17
 * UniqueValidator checks if the value being validated is unique in the table column specified by
18
 * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].
19
 *
20
 * The following are examples of validation rules using this validator:
21
 *
22
 * ```php
23
 * // a1 needs to be unique
24
 * ['a1', 'unique']
25
 * // a1 needs to be unique, but column a2 will be used to check the uniqueness of the a1 value
26
 * ['a1', 'unique', 'targetAttribute' => 'a2']
27
 * // a1 and a2 need to be unique together, and they both will receive error message
28
 * [['a1', 'a2'], 'unique', 'targetAttribute' => ['a1', 'a2']]
29
 * // a1 and a2 need to be unique together, only a1 will receive error message
30
 * ['a1', 'unique', 'targetAttribute' => ['a1', 'a2']]
31
 * // a1 needs to be unique by checking the uniqueness of both a2 and a3 (using a1 value)
32
 * ['a1', 'unique', 'targetAttribute' => ['a2', 'a1' => 'a3']]
33
 * ```
34
 *
35
 * @author Qiang Xue <[email protected]>
36
 * @since 2.0
37
 */
38
class UniqueValidator extends Validator
39
{
40
    /**
41
     * @var string the name of the ActiveRecord class that should be used to validate the uniqueness
42
     * of the current attribute value. If not set, it will use the ActiveRecord class of the attribute being validated.
43
     * @see targetAttribute
44
     */
45
    public $targetClass;
46
    /**
47
     * @var string|array the name of the ActiveRecord attribute that should be used to
48
     * validate the uniqueness of the current attribute value. If not set, it will use the name
49
     * of the attribute currently being validated. You may use an array to validate the uniqueness
50
     * of multiple columns at the same time. The array values are the attributes that will be
51
     * used to validate the uniqueness, while the array keys are the attributes whose values are to be validated.
52
     * If the key and the value are the same, you can just specify the value.
53
     */
54
    public $targetAttribute;
55
    /**
56
     * @var string|array|\Closure additional filter to be applied to the DB query used to check the uniqueness of the attribute value.
57
     * This can be a string or an array representing the additional query condition (refer to [[\yii\db\Query::where()]]
58
     * on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`
59
     * is the [[\yii\db\Query|Query]] object that you can modify in the function.
60
     */
61
    public $filter;
62
    /**
63
     * @var string the user-defined error message. When validating single attribute, it may contain
64
     * the following placeholders which will be replaced accordingly by the validator:
65
     *
66
     * - `{attribute}`: the label of the attribute being validated
67
     * - `{value}`: the value of the attribute being validated
68
     *
69
     * When validating mutliple attributes, it may contain the following placeholders:
70
     *
71
     * - `{attributes}`: the labels of the attributes being validated.
72
     * - `{values}`: the values of the attributes being validated.
73
     *
74
     */
75
    public $message;
76
    /**
77
     * @var string
78
     * @since 2.0.9
79
     * @deprecated Deprecated since version 2.0.10, to be removed in 2.1. Use [[message]] property
80
     * to setup custom message for multiple target attributes.
81
     */
82
    public $comboNotUnique;
83
84
85
    /**
86
     * @inheritdoc
87
     */
88 36
    public function init()
89
    {
90 36
        parent::init();
91 36
        if ($this->message !== null) {
92 3
            return;
93
        }
94 36
        if (is_array($this->targetAttribute) && count($this->targetAttribute) > 1) {
95
            // fallback for deprecated `comboNotUnique` property - use it as message if is set
96 6
            if ($this->comboNotUnique === null) {
0 ignored issues
show
Deprecated Code introduced by
The property yii\validators\UniqueValidator::$comboNotUnique has been deprecated with message: Deprecated since version 2.0.10, to be removed in 2.1. Use [[message]] property
to setup custom message for multiple target attributes.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
97 3
                $this->message = Yii::t('yii', 'The combination {values} of {attributes} has already been taken.');
98 3
            } else {
99 3
                $this->message = $this->comboNotUnique;
0 ignored issues
show
Deprecated Code introduced by
The property yii\validators\UniqueValidator::$comboNotUnique has been deprecated with message: Deprecated since version 2.0.10, to be removed in 2.1. Use [[message]] property
to setup custom message for multiple target attributes.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
100
            }
101 6
        } else {
102 33
            $this->message = Yii::t('yii', '{attribute} "{value}" has already been taken.');
103
        }
104 36
    }
105
106
    /**
107
     * @inheritdoc
108
     */
109 33
    public function validateAttribute($model, $attribute)
110
    {
111
        /* @var $targetClass ActiveRecordInterface */
112 33
        $targetClass = $this->targetClass === null ? get_class($model) : $this->targetClass;
113 33
        $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
114
115 33
        if (is_array($targetAttribute)) {
116 12
            $params = [];
117 12
            foreach ($targetAttribute as $k => $v) {
118 12
                $params[$v] = is_int($k) ? $model->$v : $model->$k;
119 12
            }
120 12
        } else {
121 27
            $params = [$targetAttribute => $model->$attribute];
122
        }
123
124 33
        foreach ($params as $value) {
125 33
            if (is_array($value)) {
126 6
                $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
127
128 6
                return;
129
            }
130 30
        }
131
132 30
        $query = $targetClass::find();
133 30
        $query->andWhere($params);
134
135 30
        if ($this->filter instanceof \Closure) {
136
            call_user_func($this->filter, $query);
137 30
        } elseif ($this->filter !== null) {
138
            $query->andWhere($this->filter);
139
        }
140
141 30
        if (!$model instanceof ActiveRecordInterface || $model->getIsNewRecord() || $model->className() !== $targetClass::className()) {
142
            // if current $model isn't in the database yet then it's OK just to call exists()
143
            // also there's no need to run check based on primary keys, when $targetClass is not the same as $model's class
144 27
            $exists = $query->exists();
145 24
        } else {
146
            // if current $model is in the database already we can't use exists()
147
            /* @var $models ActiveRecordInterface[] */
148 13
            $models = $query->limit(2)->all();
149 13
            $n = count($models);
150 13
            if ($n === 1) {
151 13
                $keys = array_keys($params);
152 13
                $pks = $targetClass::primaryKey();
153 13
                sort($keys);
154 13
                sort($pks);
155 13
                if ($keys === $pks) {
156
                    // primary key is modified and not unique
157 9
                    $exists = $model->getOldPrimaryKey() != $model->getPrimaryKey();
158 9
                } else {
159
                    // non-primary key, need to exclude the current record based on PK
160 7
                    $exists = reset($models)->getPrimaryKey() != $model->getOldPrimaryKey();
161
                }
162 13
            } else {
163 3
                $exists = $n > 1;
164
            }
165
        }
166
167 27
        if ($exists) {
168 23
            if (count($targetAttribute) > 1) {
169 6
                $this->addComboNotUniqueError($model, $attribute);
170 6
            } else {
171 23
                $this->addError($model, $attribute, $this->message);
172
            }
173 23
        }
174 27
    }
175
176
    /**
177
     * Builds and adds [[comboNotUnique]] error message to the specified model attribute.
178
     * @param \yii\base\Model $model the data model.
179
     * @param string $attribute the name of the attribute.
180
     */
181 6
    private function addComboNotUniqueError($model, $attribute)
182
    {
183 6
        $attributeCombo = [];
184 6
        $valueCombo = [];
185 6
        foreach ($this->targetAttribute as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $this->targetAttribute of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
186 6
            if(is_int($key)) {
187 6
                $attributeCombo[] = $model->getAttributeLabel($value);
188 6
                $valueCombo[] = '"' . $model->$value . '"';
189 6
            } else {
190
                $attributeCombo[] = $model->getAttributeLabel($key);
191
                $valueCombo[] = '"' . $model->$key . '"';
192
            }
193 6
        }
194 6
        $this->addError($model, $attribute, $this->message, [
195 6
            'attributes' => Inflector::sentence($attributeCombo),
196 6
            'values' => implode('-', $valueCombo)
197 6
        ]);
198 6
    }
199
}
200