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 ( 1501c6...0e2b6c )
by Robert
12:51
created

ExistValidator::applyTableAlias()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
ccs 13
cts 13
cp 1
cc 4
eloc 14
nc 6
nop 3
crap 4
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\base\Model;
13
use yii\db\ActiveQuery;
14
use yii\db\ActiveRecord;
15
16
/**
17
 * ExistValidator validates that the attribute value exists in a table.
18
 *
19
 * ExistValidator checks if the value being validated can be found in the table column specified by
20
 * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].
21
 *
22
 * This validator is often used to verify that a foreign key contains a value
23
 * that can be found in the foreign table.
24
 *
25
 * The following are examples of validation rules using this validator:
26
 *
27
 * ```php
28
 * // a1 needs to exist
29
 * ['a1', 'exist']
30
 * // a1 needs to exist, but its value will use a2 to check for the existence
31
 * ['a1', 'exist', 'targetAttribute' => 'a2']
32
 * // a1 and a2 need to exist together, and they both will receive error message
33
 * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']]
34
 * // a1 and a2 need to exist together, only a1 will receive error message
35
 * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']]
36
 * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value)
37
 * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']]
38
 * ```
39
 *
40
 * @author Qiang Xue <[email protected]>
41
 * @since 2.0
42
 */
43
class ExistValidator extends Validator
44
{
45
    /**
46
     * @var string the name of the ActiveRecord class that should be used to validate the existence
47
     * of the current attribute value. If not set, it will use the ActiveRecord class of the attribute being validated.
48
     * @see targetAttribute
49
     */
50
    public $targetClass;
51
    /**
52
     * @var string|array the name of the ActiveRecord attribute that should be used to
53
     * validate the existence of the current attribute value. If not set, it will use the name
54
     * of the attribute currently being validated. You may use an array to validate the existence
55
     * of multiple columns at the same time. The array key is the name of the attribute with the value to validate,
56
     * the array value is the name of the database field to search.
57
     */
58
    public $targetAttribute;
59
    /**
60
     * @var string|array|\Closure additional filter to be applied to the DB query used to check the existence of the attribute value.
61
     * This can be a string or an array representing the additional query condition (refer to [[\yii\db\Query::where()]]
62
     * on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`
63
     * is the [[\yii\db\Query|Query]] object that you can modify in the function.
64
     */
65
    public $filter;
66
    /**
67
     * @var bool whether to allow array type attribute.
68
     */
69
    public $allowArray = false;
70
    /**
71
     * @var string and|or define how target attributes are related
72
     * @since 2.0.11
73
     */
74
    public $targetAttributeJunction = 'and';
75
76
77
    /**
78
     * @inheritdoc
79
     */
80 18
    public function init()
81
    {
82 18
        parent::init();
83 18
        if ($this->message === null) {
84 18
            $this->message = Yii::t('yii', '{attribute} is invalid.');
85
        }
86 18
    }
87
88
    /**
89
     * @inheritdoc
90
     */
91 12
    public function validateAttribute($model, $attribute)
92
    {
93 12
        $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
94 12
        $params = $this->prepareConditions($targetAttribute, $model, $attribute);
95 12
        $conditions[] = $this->targetAttributeJunction == 'or' ? 'or' : 'and';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$conditions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $conditions = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
96
97 12
        if (!$this->allowArray) {
98 12
            foreach ($params as $key => $value) {
99 12
                if (is_array($value)) {
100 3
                    $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
101
102 3
                    return;
103
                }
104 12
                $conditions[] = [$key => $value];
105
            }
106
        } else {
107 3
            $conditions[] = $params;
108
        }
109
110 12
        $targetClass = $this->targetClass === null ? get_class($model) : $this->targetClass;
111 12
        $query = $this->createQuery($targetClass, $conditions);
112
113 12
        if (is_array($model->$attribute)) {
114 3
            if ($query->count("DISTINCT [[$targetAttribute]]") != count($model->$attribute)) {
115 3
                $this->addError($model, $attribute, $this->message);
116
            }
117 12
        } elseif (!$query->exists()) {
118 6
            $this->addError($model, $attribute, $this->message);
119
        }
120 12
    }
121
122
    /**
123
     * Processes attributes' relations described in $targetAttribute parameter into conditions, compatible with
124
     * [[\yii\db\Query::where()|Query::where()]] key-value format.
125
     *
126
     * @param $targetAttribute array|string $attribute the name of the ActiveRecord attribute that should be used to
127
     * validate the existence of the current attribute value. If not set, it will use the name
128
     * of the attribute currently being validated. You may use an array to validate the existence
129
     * of multiple columns at the same time. The array key is the name of the attribute with the value to validate,
130
     * the array value is the name of the database field to search.
131
     * If the key and the value are the same, you can just specify the value.
132
     * @param \yii\base\Model $model the data model to be validated
133
     * @param string $attribute the name of the attribute to be validated in the $model
134
     * @return array conditions, compatible with [[\yii\db\Query::where()|Query::where()]] key-value format.
135
     * @throws InvalidConfigException
136
     */
137 12
    private function prepareConditions($targetAttribute, $model, $attribute)
138
    {
139 12
        if (is_array($targetAttribute)) {
140 9
            if ($this->allowArray) {
141
                throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
142
            }
143 9
            $conditions = [];
144 9
            foreach ($targetAttribute as $k => $v) {
145 9
                $conditions[$v] = is_int($k) ? $model->$v : $model->$k;
146
            }
147
        } else {
148 3
            $conditions = [$targetAttribute => $model->$attribute];
149
        }
150
151 12
        $targetModelClass = $this->getTargetClass($model);
152 12
        if (!is_subclass_of($targetModelClass, 'yii\db\ActiveRecord')) {
153
            return $conditions;
154
        }
155
156
        /** @var ActiveRecord $targetModelClass */
157 12
        return $this->applyTableAlias($targetModelClass::find(), $conditions);
158
    }
159
160
    /**
161
     * @param Model $model the data model to be validated
162
     * @return string Target class name
163
     */
164 12
    private function getTargetClass($model)
165
    {
166 12
        return $this->targetClass === null ? get_class($model) : $this->targetClass;
167
    }
168
169
    /**
170
     * @inheritdoc
171
     */
172 6
    protected function validateValue($value)
173
    {
174 6
        if ($this->targetClass === null) {
175 3
            throw new InvalidConfigException('The "targetClass" property must be set.');
176
        }
177 6
        if (!is_string($this->targetAttribute)) {
178 3
            throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
179
        }
180
181 3
        $query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);
182
183 3
        if (is_array($value)) {
184 3
            if (!$this->allowArray) {
185 3
                return [$this->message, []];
186
            }
187
188
            return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ? null : [$this->message, []];
189
        }
190
191 3
        return $query->exists() ? null : [$this->message, []];
192
    }
193
194
    /**
195
     * Creates a query instance with the given condition.
196
     * @param string $targetClass the target AR class
197
     * @param mixed $condition query condition
198
     * @return \yii\db\ActiveQueryInterface the query instance
199
     */
200 15
    protected function createQuery($targetClass, $condition)
201
    {
202
        /* @var $targetClass \yii\db\ActiveRecordInterface */
203 15
        $query = $targetClass::find()->andWhere($condition);
204 15
        if ($this->filter instanceof \Closure) {
205
            call_user_func($this->filter, $query);
206 15
        } elseif ($this->filter !== null) {
207
            $query->andWhere($this->filter);
208
        }
209
210 15
        return $query;
211
    }
212
213
    /**
214
     * Returns conditions with alias.
215
     * @param ActiveQuery $query
216
     * @param array $conditions array of condition, keys to be modified
217
     * @param null|string $alias set empty string for no apply alias. Set null for apply primary table alias
218
     * @return array
219
     */
220 12
    private function applyTableAlias($query, $conditions, $alias = null)
221
    {
222 12
        if ($alias === null) {
223 12
            $alias = array_keys($query->getTablesUsedInFrom())[0];
224
        }
225 12
        $prefixedConditions = [];
226 12
        foreach ($conditions as $columnName => $columnValue) {
227 12
            if (strpos($columnName, '(') === false) {
228 9
                $prefixedColumn = "{$alias}.[[" . preg_replace(
229 9
                    '/^' . preg_quote($alias) . '\.(.*)$/',
230 9
                    '$1',
231 9
                    $columnName) . ']]';
232
            } else {
233
                // there is an expression, can't prefix it reliably
234 3
                $prefixedColumn = $columnName;
235
            }
236
237 12
            $prefixedConditions[$prefixedColumn] = $columnValue;
238
        }
239
240 12
        return $prefixedConditions;
241
    }
242
}
243