Passed
Push — master ( 3bbc21...388305 )
by Alexander
02:02
created

CompositeValidator::validateValue()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 23
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 6
nop 1
crap 56
1
<?php
2
3
/**
4
 * Copied from
5
 * Author: paulzi
6
 *
7
 * https://gist.github.com/paulzi/ad27c4689475ca442a2ea5880d659ff3
8
 */
9
10
namespace Horat1us\Yii\Validators;
11
12
use yii\base\InvalidConfigException;
13
use Yii;
14
use yii\base\Model;
15
use yii\helpers\ArrayHelper;
16
use yii\validators\Validator;
17
18
class CompositeValidator extends Validator
19
{
20
    /**
21
     * @var array
22
     */
23
    public $rules = [];
24
    /**
25
     * @var boolean
26
     */
27
    public $allowMessageFromRule = true;
28
    /**
29
     * @var Validator[]
30
     */
31
    private $_validators = [];
32
33
    /**
34
     * @inheritdoc
35
     */
36
    public function init()
37
    {
38
        parent::init();
39
        if ($this->message === null) {
40
            $this->message = Yii::t('yii', '{attribute} is invalid.');
41
        }
42
    }
43
44
    /**
45
     * @param integer $index
46
     * @param Model|null $model
47
     * @return Validator
48
     */
49
    private function getValidator($index, $model = null)
50
    {
51
        if (!isset($this->_validators[$index])) {
52
            $this->_validators[$index] = $this->createEmbeddedValidator($this->rules[$index], $model);
53
        }
54
        return $this->_validators[$index];
55
    }
56
57
    /**
58
     * @param array $rule
59
     * @param Model|null $model
60
     * @throws \yii\base\InvalidConfigException
61
     * @return Validator validator instance
62
     */
63
    private function createEmbeddedValidator($rule, $model)
64
    {
65
        if ($rule instanceof Validator) {
66
            return $rule;
67
        } elseif (is_array($rule) && isset($rule[0]) && isset($rule[1])) {
68
            if (!is_object($model)) {
69
                $model = new Model(); // mock up context model
70
            }
71
            return Validator::createValidator($rule[1], $model, $this->attributes, array_slice($rule, 2));
72
        } else {
73
            throw new InvalidConfigException('Invalid validation rule: a rule must be an array specifying validator type.');
74
        }
75
    }
76
77
    /**
78
     * @param Model $model
79
     * @param string $attribute
80
     * @param Validator $validator
81
     * @param array $originalErrors
82
     * @param array $value
83
     * @param string $target
84
     */
85
    private function validateInternal(&$model, &$attribute, &$validator, &$originalErrors, &$value, $target)
86
    {
87
        $current = explode('[]', $target, 2);
88
        if (count($current) > 1) {
89
            $items = ArrayHelper::getValue($value, $current[0]);
90
            if ($items) {
91
                foreach ($items as $i => $item) {
92
                    $this->validateInternal($model, $attribute, $validator, $originalErrors, $value, "{$current[0]}.{$i}{$current[1]}");
93
                }
94
            }
95
        } else {
96
            $v = $model->$attribute = ArrayHelper::getValue($value, $target);
97
            if (!$validator->skipOnEmpty || !$validator->isEmpty($v)) {
98
                $validator->validateAttribute($model, $attribute);
99
            }
100
            ArrayHelper::setValue($value, $target, $model->$attribute);
101
            if ($model->hasErrors($attribute)) {
102
                $validationErrors = $model->getErrors($attribute);
103
                $model->clearErrors($attribute);
104
                if (!empty($originalErrors)) {
105
                    $model->addErrors([$attribute => $originalErrors]);
106
                }
107
                if ($this->allowMessageFromRule) {
108
                    $name = "{$attribute}.{$target}";
109
                    $name = preg_replace('/\.(\w+)/', '[\\1]', $name);
110
                    $model->addErrors([$name => $validationErrors]);
111
                } else {
112
                    $this->addError($model, $attribute, $this->message, ['value' => $v]);
113
                }
114
            }
115
            $model->$attribute = $value;
116
        }
117
    }
118
119
    /**
120
     * @inheritdoc
121
     */
122
    public function validateAttribute($model, $attribute)
123
    {
124
        $value = $model->$attribute;
125
        foreach ($this->rules as $index => $rule) {
126
            $validator = $this->getValidator($index, $model);
127
            $originalErrors = $model->getErrors($attribute);
128
            $targets = (array)$rule[0];
129
            foreach ($targets as $target) {
130
                $this->validateInternal($model, $attribute, $validator, $originalErrors, $value, $target);
131
            }
132
            $model->$attribute = $value;
133
        }
134
    }
135
136
    /**
137
     * @inheritdoc
138
     */
139
    protected function validateValue($value)
140
    {
141
        foreach ($this->rules as $index => $rule) {
142
            $validator = $this->getValidator($index);
143
            $targets = (array)$rule[0];
144
            foreach ($targets as $target) {
145
                $v = ArrayHelper::getValue($value, $target);
146
                if ($validator->skipOnEmpty && $validator->isEmpty($v)) {
147
                    continue;
148
                }
149
                $result = $validator->validateValue($v);
150
                if ($result !== null) {
151
                    if ($this->allowMessageFromRule) {
152
                        $result[1]['value'] = $v;
153
                        return $result;
154
                    } else {
155
                        return [$this->message, ['value' => $v]];
156
                    }
157
                }
158
            }
159
        }
160
        return null;
161
    }
162
}
163