Completed
Push — 11139-each-validator-attribute... ( a0e614 )
by Dmitry
08:34
created

EachValidator::validateValue()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 24
ccs 15
cts 15
cp 1
rs 6.7272
cc 7
eloc 15
nc 6
nop 1
crap 7
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\base\InvalidConfigException;
11
use Yii;
12
use yii\base\Model;
13
14
/**
15
 * EachValidator validates an array by checking each of its elements against an embedded validation rule.
16
 *
17
 * ```php
18
 * class MyModel extends Model
19
 * {
20
 *     public $categoryIDs = [];
21
 *
22
 *     public function rules()
23
 *     {
24
 *         return [
25
 *             // checks if every category ID is an integer
26
 *             ['categoryIDs', 'each', 'rule' => ['integer']],
27
 *         ]
28
 *     }
29
 * }
30
 * ```
31
 *
32
 * > Note: This validator will not work with inline validation rules in case of usage outside the model scope,
33
 *   e.g. via [[validate()]] method.
34
 *
35
 * @author Paul Klimov <[email protected]>
36
 * @since 2.0.4
37
 */
38
class EachValidator extends Validator
39
{
40
    /**
41
     * @var array|Validator definition of the validation rule, which should be used on array values.
42
     * It should be specified in the same format as at [[yii\base\Model::rules()]], except it should not
43
     * contain attribute list as the first element.
44
     * For example:
45
     *
46
     * ```php
47
     * ['integer']
48
     * ['match', 'pattern' => '/[a-z]/is']
49
     * ```
50
     *
51
     * Please refer to [[yii\base\Model::rules()]] for more details.
52
     */
53
    public $rule;
54
    /**
55
     * @var boolean whether to use error message composed by validator declared via [[rule]] if its validation fails.
56
     * If enabled, error message specified for this validator itself will appear only if attribute value is not an array.
57
     * If disabled, own error message value will be used always.
58
     */
59
    public $allowMessageFromRule = true;
60
61
    /**
62
     * @var Validator validator instance.
63
     */
64
    private $_validator;
65
66
67
    /**
68
     * @inheritdoc
69
     */
70 6
    public function init()
71
    {
72 6
        parent::init();
73 6
        if ($this->message === null) {
74 6
            $this->message = Yii::t('yii', '{attribute} is invalid.');
75 6
        }
76 6
    }
77
78
    /**
79
     * Returns the validator declared in [[rule]].
80
     * @param Model|null $model model in which context validator should be created.
81
     * @return Validator the declared validator.
82
     */
83 6
    private function getValidator($model = null)
84
    {
85 6
        if ($this->_validator === null) {
86 6
            $this->_validator = $this->createEmbeddedValidator($model);
87 6
        }
88 6
        return $this->_validator;
89
    }
90
91
    /**
92
     * Creates validator object based on the validation rule specified in [[rule]].
93
     * @param Model|null $model model in which context validator should be created.
94
     * @throws \yii\base\InvalidConfigException
95
     * @return Validator validator instance
96
     */
97 6
    private function createEmbeddedValidator($model)
98
    {
99 6
        $rule = $this->rule;
100 6
        if ($rule instanceof Validator) {
101
            return $rule;
102 6
        } elseif (is_array($rule) && isset($rule[0])) { // validator type
103 6
            if (!is_object($model)) {
104 6
                $model = new Model(); // mock up context model
105 6
            }
106 6
            return Validator::createValidator($rule[0], $model, $this->attributes, array_slice($rule, 1));
107
        } else {
108
            throw new InvalidConfigException('Invalid validation rule: a rule must be an array specifying validator type.');
109
        }
110
    }
111
112
    /**
113
     * @inheritdoc
114
     */
115 3
    public function validateAttribute($model, $attribute)
116
    {
117 3
        $value = $model->$attribute;
118 3
        $validator = $this->getValidator();
119 3
        if ($validator instanceof FilterValidator && is_array($value)) {
120 1
            $filteredValue = [];
121 1
            foreach ($value as $k => $v) {
122 1
                if (!$validator->skipOnArray || !is_array($v)) {
123 1
                    $filteredValue[$k] = call_user_func($validator->filter, $v);
124 1
                }
125 1
            }
126 1
            $model->$attribute = $filteredValue;
127 1
        } else {
128 2
            $this->getValidator($model); // ensure model context while validator creation
129 2
            parent::validateAttribute($model, $attribute);
130
        }
131 3
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136 5
    protected function validateValue($value)
137
    {
138 5
        if (!is_array($value)) {
139 1
            return [$this->message, []];
140
        }
141
142 5
        $validator = $this->getValidator();
143 5
        foreach ($value as $v) {
144 5
            if ($validator->skipOnEmpty && $validator->isEmpty($v)) {
145 1
                continue;
146
            }
147 5
            $result = $validator->validateValue($v);
148 5
            if ($result !== null) {
149 4
                if ($this->allowMessageFromRule) {
150 4
                    $result[1]['value'] = $v;
151 4
                    return $result;
152
                } else {
153 2
                    return [$this->message, ['value' => $v]];
154
                }
155
            }
156 3
        }
157
158 3
        return null;
159
    }
160
}
161