Completed
Push — master ( 1644a3...6b640a )
by Tomasz
02:29
created

StdValidator::processArrayField()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 3
nop 2
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Hop\Validator;
6
7
use Hop\Validator\Message\MessageInterface;
8
use Hop\Validator\Message\FieldMessage;
9
use Hop\Validator\Message\MessagesContainer;
10
use Hop\Validator\Strategy\Field;
11
use Hop\Validator\Strategy\FieldInterface;
12
use Hop\Validator\Strategy\Strategy;
13
use Hop\Validator\Strategy\StructureField;
14
use Hop\Validator\Validator\RuleValidator;
15
16
/**
17
 * Class StdValidator
18
 * @package Application\Service\Validator
19
 */
20
class StdValidator implements Validator
21
{
22
    /**
23
     * @var RuleValidator[]
24
     */
25
    private $validators = [];
26
27
    /**
28
     * @param string $validatorName
29
     * @param RuleValidator $validator
30
     */
31 10
    public function registerRuleValidator(string $validatorName, RuleValidator $validator): void
32
    {
33 10
        $this->validators[$validatorName] = $validator;
34 10
    }
35
36
    /**
37
     * @param array $config
38
     * @return StdValidator
39
     */
40 1
    public static function fromConfig(array $config): self
41
    {
42 1
        $validator = new static;
43 1
        foreach ($config as $validatorName => $validatorClassName) {
44 1
            if (!\class_exists($validatorClassName)) {
45 1
                throw new \InvalidArgumentException(sprintf('Validator class %s does not exist', $validatorClassName));
46
            }
47
            $validator->registerRuleValidator($validatorName, new $validatorClassName);
48
        }
49
        return $validator;
50
    }
51
52
    /**
53
     * @param array $data
54
     * @param Strategy $strategy
55
     * @return bool
56
     * @throws \InvalidArgumentException
57
     */
58 11
    public function isValid(array $data, Strategy $strategy): bool
59
    {
60 11
        return \count($this->getMessages($data, $strategy)) === 0;
61
    }
62
63
    /**
64
     * @param array $data
65
     * @param Strategy $strategy
66
     * @return \Hop\Validator\Message\MessagesContainer
67
     */
68 11
    public function getMessages(array $data, Strategy $strategy): MessagesContainer
69
    {
70 11
        $messages = new MessagesContainer();
71
72 11
        foreach ($strategy->getFields() as $field) {
73 11
            if ($field->condition() !== null && !$field->condition()($data)) {
74 1
                continue;
75
            }
76
77 10
            if ($this->detectEmpty($data, $field->fieldName())) {
78 3
                if (!$field->required()) {
79 1
                    continue;
80
                }
81
82 2
                $messages->attachMessage($field->fieldName(), (new FieldMessage())->attachMessage('NotEmpty', 'Value cannot be empty'));
83
            }
84
85 9
            $messages->attachMessage(
86 9
                $field->fieldName(),
87 9
                $field->isArray() ?
88 3
                    $this->processArrayField($field, $data[$field->fieldName()]) :
89 9
                    $this->processSingleField(@$data[$field->fieldName()], $field)
90
            );
91
        }
92 10
        return $messages;
93
    }
94
95
    /**
96
     * @param string $name
97
     * @return RuleValidator
98
     * @throws \InvalidArgumentException
99
     */
100 9
    private function getRuleValidator(string $name): RuleValidator
101
    {
102 9
        if (!isset($this->validators[$name])) {
103 1
            throw new \InvalidArgumentException(sprintf('Validator %s not registered', $name));
104
        }
105
106 8
        return $this->validators[$name];
107
    }
108
109
    /**
110
     * @param array $data
111
     * @param string $fieldName
112
     * @return bool
113
     */
114 10
    private function detectEmpty(array $data, string $fieldName): bool
115
    {
116 10
        if (!array_key_exists($fieldName, $data)) {
117 3
            return true;
118
        }
119
120 8
        if ($data[$fieldName] === null) {
121 1
            return true;
122
        }
123
124 8
        if (\is_string($data[$fieldName]) && $data[$fieldName] === '') {
125 1
            return true;
126
        }
127
128 7
        return false;
129
    }
130
131 9
    private function processSingleField($row, FieldInterface $field): MessageInterface
132
    {
133
        // TODO introduce policies/strategies if required
134
        // for now it might be overengineering
135
        switch (true) {
136 9
            case $field instanceof Field:
137 9
                return $this->processField($field, $row);
138 2
            case $field instanceof StructureField:
139 2
                return $this->getMessages($row, $field->strategy());
140
            default:
141
                throw new \DomainException('Unknown field type');
142
        }
143
    }
144
145 9
    private function processField(Field $field, $row): FieldMessage
146
    {
147 9
        $messages = new FieldMessage();
148 9
        foreach ($field->validators() as $validatorName => $options) {
149 9
            $ruleValidator = $this->getRuleValidator($validatorName);
150 8
            if (!$ruleValidator->isValid($row, $options)) {
151 8
                $messages->attachMessage($validatorName, $ruleValidator->getMessage($row, $options));
152
            }
153
        }
154 8
        return $messages;
155
    }
156
157 3
    private function processArrayField(FieldInterface $field, $data) : MessageInterface
158
    {
159 3
        $messages = new MessagesContainer();
160 3
        if (!\is_array($data)) {
161 1
            return (new FieldMessage())->attachMessage('NotArray', 'Value is not an array');
162
        }
163
164 3
        foreach ($data as $index => $row) {
165 3
            $messages->attachMessage((string)$index, $this->processSingleField($row, $field));
166
        }
167 3
        return $messages;
168
    }
169
}
170