Completed
Push — activequery-alias2 ( d7e45e...ce3573 )
by Carsten
39:33 queued 33:21
created

ExistValidator::init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2
Metric Value
dl 0
loc 7
rs 9.4285
ccs 6
cts 6
cp 1
cc 2
eloc 4
nc 2
nop 0
crap 2
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
13
/**
14
 * ExistValidator validates that the attribute value exists in a table.
15
 *
16
 * ExistValidator checks if the value being validated can be found in the table column specified by
17
 * the ActiveRecord class [[targetClass]] and the attribute [[targetAttribute]].
18
 *
19
 * This validator is often used to verify that a foreign key contains a value
20
 * that can be found in the foreign table.
21
 *
22
 * The following are examples of validation rules using this validator:
23
 *
24
 * ```php
25
 * // a1 needs to exist
26
 * ['a1', 'exist']
27
 * // a1 needs to exist, but its value will use a2 to check for the existence
28
 * ['a1', 'exist', 'targetAttribute' => 'a2']
29
 * // a1 and a2 need to exist together, and they both will receive error message
30
 * [['a1', 'a2'], 'exist', 'targetAttribute' => ['a1', 'a2']]
31
 * // a1 and a2 need to exist together, only a1 will receive error message
32
 * ['a1', 'exist', 'targetAttribute' => ['a1', 'a2']]
33
 * // a1 needs to exist by checking the existence of both a2 and a3 (using a1 value)
34
 * ['a1', 'exist', 'targetAttribute' => ['a2', 'a1' => 'a3']]
35
 * ```
36
 *
37
 * @author Qiang Xue <[email protected]>
38
 * @since 2.0
39
 */
40
class ExistValidator extends Validator
41
{
42
    /**
43
     * @var string the name of the ActiveRecord class that should be used to validate the existence
44
     * of the current attribute value. It not set, it will use the ActiveRecord class of the attribute being validated.
45
     * @see targetAttribute
46
     */
47
    public $targetClass;
48
    /**
49
     * @var string|array the name of the ActiveRecord attribute that should be used to
50
     * validate the existence of the current attribute value. If not set, it will use the name
51
     * of the attribute currently being validated. You may use an array to validate the existence
52
     * of multiple columns at the same time. The array values are the attributes that will be
53
     * used to validate the existence, while the array keys are the attributes whose values are to be validated.
54
     * If the key and the value are the same, you can just specify the value.
55
     */
56
    public $targetAttribute;
57
    /**
58
     * @var string|array|\Closure additional filter to be applied to the DB query used to check the existence of the attribute value.
59
     * This can be a string or an array representing the additional query condition (refer to [[\yii\db\Query::where()]]
60
     * on the format of query condition), or an anonymous function with the signature `function ($query)`, where `$query`
61
     * is the [[\yii\db\Query|Query]] object that you can modify in the function.
62
     */
63
    public $filter;
64
    /**
65
     * @var boolean whether to allow array type attribute.
66
     */
67
    public $allowArray = false;
68
69
70
    /**
71
     * @inheritdoc
72
     */
73 12
    public function init()
74
    {
75 12
        parent::init();
76 12
        if ($this->message === null) {
77 12
            $this->message = Yii::t('yii', '{attribute} is invalid.');
78 12
        }
79 12
    }
80
81
    /**
82
     * @inheritdoc
83
     */
84 6
    public function validateAttribute($model, $attribute)
85
    {
86 6
        $targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
87
88 6
        if (is_array($targetAttribute)) {
89 3
            if ($this->allowArray) {
90
                throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
91
            }
92 3
            $params = [];
93 3
            foreach ($targetAttribute as $k => $v) {
94 3
                $params[$v] = is_int($k) ? $model->$v : $model->$k;
95 3
            }
96 3
        } else {
97 3
            $params = [$targetAttribute => $model->$attribute];
98
        }
99
100 6
        if (!$this->allowArray) {
101 6
            foreach ($params as $value) {
102 6
                if (is_array($value)) {
103 3
                    $this->addError($model, $attribute, Yii::t('yii', '{attribute} is invalid.'));
104
105 3
                    return;
106
                }
107 6
            }
108 6
        }
109
110 6
        $targetClass = $this->targetClass === null ? get_class($model) : $this->targetClass;
111 6
        $query = $this->createQuery($targetClass, $params);
112
113 6
        if (is_array($model->$attribute)) {
114 3
            if ($query->count("DISTINCT [[$targetAttribute]]") != count($model->$attribute)) {
115 3
                $this->addError($model, $attribute, $this->message);
116 3
            }
117 6
        } elseif (!$query->exists()) {
118 6
            $this->addError($model, $attribute, $this->message);
119 6
        }
120 6
    }
121
122
    /**
123
     * @inheritdoc
124
     */
125 6
    protected function validateValue($value)
126
    {
127 6
        if ($this->targetClass === null) {
128 3
            throw new InvalidConfigException('The "targetClass" property must be set.');
129
        }
130 6
        if (!is_string($this->targetAttribute)) {
131 3
            throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
132
        }
133
134 3
        $query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);
135
136 3
        if (is_array($value)) {
137 3
            if (!$this->allowArray) {
138 3
                return [$this->message, []];
139
            }
140
            return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ? null : [$this->message, []];
141
        } else {
142 3
            return $query->exists() ? null : [$this->message, []];
143
        }
144
    }
145
146
    /**
147
     * Creates a query instance with the given condition.
148
     * @param string $targetClass the target AR class
149
     * @param mixed $condition query condition
150
     * @return \yii\db\ActiveQueryInterface the query instance
151
     */
152 9
    protected function createQuery($targetClass, $condition)
153
    {
154
        /* @var $targetClass \yii\db\ActiveRecordInterface */
155 9
        $query = $targetClass::find()->andWhere($condition);
156 9
        if ($this->filter instanceof \Closure) {
157
            call_user_func($this->filter, $query);
158 9
        } elseif ($this->filter !== null) {
159
            $query->andWhere($this->filter);
160
        }
161
162 9
        return $query;
163
    }
164
}
165