Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
98 / 98 |
|
100.00% |
4 / 4 |
CRAP | |
100.00% |
1 / 1 |
UpdateSchema | |
100.00% |
98 / 98 |
|
100.00% |
4 / 4 |
20 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
addValidateResults | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
main | |
100.00% |
93 / 93 |
|
100.00% |
1 / 1 |
17 |
1 | <?php |
2 | |
3 | /** |
4 | * @license MIT |
5 | * @author hazuki3417<hazuki3417@gmail.com> |
6 | * @copyright 2022 hazuki3417 all rights reserved. |
7 | */ |
8 | |
9 | namespace Selen\MongoDB\Validator; |
10 | |
11 | use ReflectionClass; |
12 | use Selen\Data\ArrayPath; |
13 | use Selen\MongoDB\Attribute\SchemaLoader; |
14 | use Selen\MongoDB\Attributes\Nest; |
15 | use Selen\MongoDB\Validator\Model\ValidateResult; |
16 | use Selen\MongoDB\Validator\Model\ValidatorResult; |
17 | |
18 | class UpdateSchema implements SchemaValidatorInterface |
19 | { |
20 | /** @var ArrayPath */ |
21 | public $arrayPath; |
22 | |
23 | /** @var Model\ValidateResult[] */ |
24 | private $validateResults = []; |
25 | |
26 | /** @var SchemaLoader */ |
27 | private $schemaLoader; |
28 | |
29 | public function __construct(SchemaLoader $schemaLoader) |
30 | { |
31 | $this->arrayPath = new ArrayPath(); |
32 | $this->schemaLoader = $schemaLoader; |
33 | } |
34 | |
35 | /** |
36 | * 値の検証を実行します |
37 | * |
38 | * @param array<mixed,mixed> $input 検証する値を渡します |
39 | */ |
40 | public function execute(array $input): ValidatorResult |
41 | { |
42 | $this->main($input); |
43 | return new ValidatorResult(...$this->validateResults); |
44 | } |
45 | |
46 | private function addValidateResults(ValidateResult ...$validateResults): void |
47 | { |
48 | $this->validateResults = \array_merge($this->validateResults, $validateResults); |
49 | } |
50 | |
51 | /** |
52 | * 値の検証を実行します |
53 | * |
54 | * @param array<mixed,mixed> $input 検証する値を渡します |
55 | */ |
56 | private function main(array $input): void |
57 | { |
58 | $this->arrayPath->down(); |
59 | |
60 | /** 検証対象の配列にのみ存在するフィールドを検出する処理 */ |
61 | $inputKeys = array_keys($input); |
62 | $definedKeys = array_keys($this->schemaLoader->fieldLoaders); |
63 | $undefinedKeys = array_diff($inputKeys, $definedKeys); |
64 | |
65 | foreach ($undefinedKeys as $undefinedKey) { |
66 | $this->arrayPath->setCurrentPath($undefinedKey); |
67 | $this->validateResults[] = new ValidateResult( |
68 | false, |
69 | ArrayPath::toString($this->arrayPath->getPaths()), |
70 | 'Undefined key.' |
71 | ); |
72 | } |
73 | |
74 | foreach ($input as $key => $value) { |
75 | $keyValue = [$key => $value]; |
76 | |
77 | if (!\array_key_exists($key, $this->schemaLoader->fieldLoaders)) { |
78 | // 入力側のkeyが定義側に存在しないとき |
79 | continue; |
80 | } |
81 | |
82 | // 入力側のkeyが定義側に存在したとき |
83 | $fieldLoader = $this->schemaLoader->fieldLoaders[$key]; |
84 | |
85 | $attributeValueValidates = $fieldLoader->fetchAttributes(ValueValidateInterface::class); |
86 | $attributeNest = $fieldLoader->attributeNest; |
87 | |
88 | $isValueValidateExecute = $attributeValueValidates !== []; |
89 | $isNestValidExecute = $attributeNest !== null; |
90 | |
91 | $key = $fieldLoader->reflectionProperty->getName(); |
92 | $this->arrayPath->setCurrentPath($key); |
93 | |
94 | if ($isValueValidateExecute) { |
95 | // 値のバリデーション処理 |
96 | $valueValidator = new Value($this->arrayPath, $attributeValueValidates); |
97 | $valueValidatorResult = $valueValidator->execute($key, $keyValue); |
98 | |
99 | if ($valueValidatorResult->failure()) { |
100 | $this->addValidateResults(...$valueValidatorResult->getValidateResults()); |
101 | // 値チェックに違反したら控えている処理は実行しない |
102 | continue; |
103 | } |
104 | |
105 | if (!$isNestValidExecute) { |
106 | // 値チェックが成功 + ネストした値のバリデーションをしない |
107 | continue; |
108 | } |
109 | |
110 | // 値チェックが成功 + ネストした値のバリデーションをする |
111 | if (!\is_array($value)) { |
112 | /** |
113 | * ここに到達するとき$valueの値は値チェックで許可されたリテラル値 |
114 | * そのためネストした値のバリデーションは実行しないようにする |
115 | * 例) null | object や null | array object といった属性の指定 |
116 | */ |
117 | $isNestValidExecute = false; |
118 | } |
119 | } |
120 | |
121 | if ($isNestValidExecute) { |
122 | // ネストした値のバリデーション処理 |
123 | /** @var Nest */ |
124 | $nestInstance = $attributeNest->newInstance(); |
125 | $schemaLoader = new SchemaLoader(new ReflectionClass($nestInstance->schemaClassName)); |
126 | $nestValidator = new self($schemaLoader); |
127 | $nestValidator->arrayPath = $this->arrayPath; |
128 | |
129 | // ネストした値 = object or array object = inputは1次元または2次元配列を期待 |
130 | if (!\is_array($value)) { |
131 | // 値がリテラル型だったとき |
132 | $format = 'Invalid value. Expect "%s" schema for array type'; |
133 | $mes = \sprintf($format, $nestInstance->schemaClassName); |
134 | $validateResult = new ValidateResult( |
135 | false, |
136 | ArrayPath::toString($this->arrayPath->getPaths()), |
137 | $mes |
138 | ); |
139 | $this->addValidateResults($validateResult); |
140 | continue; |
141 | } |
142 | |
143 | if ($nestInstance->type === Nest::TYPE_OBJECT) { |
144 | // ネストした値がobject形式 |
145 | $object = $value; |
146 | |
147 | if ($object === []) { |
148 | // 値が空配列だったとき(ネストしたobject形式の配列を期待しているため、keyは必ず存在する) |
149 | $format = 'Invalid value. Expect "%s" schema for array type'; |
150 | $mes = \sprintf($format, $nestInstance->schemaClassName); |
151 | $validateResult = new ValidateResult( |
152 | false, |
153 | ArrayPath::toString($this->arrayPath->getPaths()), |
154 | $mes |
155 | ); |
156 | $this->addValidateResults($validateResult); |
157 | continue; |
158 | } |
159 | |
160 | $nestValidatorResult = $nestValidator->execute($object); |
161 | |
162 | if ($nestValidatorResult->failure()) { |
163 | $this->addValidateResults(...$nestValidatorResult->getValidateResults()); |
164 | } |
165 | continue; |
166 | } |
167 | |
168 | // ネストした値がarray object形式 |
169 | $objects = $value; |
170 | |
171 | $nestValidator->arrayPath->down(); |
172 | |
173 | foreach ($objects as $index => $object) { |
174 | $nestValidator->arrayPath->setCurrentPath('[' . $index . ']'); |
175 | |
176 | if (!\is_array($object)) { |
177 | // 値がリテラル型だったとき |
178 | $format = 'Invalid value. Expect "%s" schema for array type'; |
179 | $mes = \sprintf($format, $nestInstance->schemaClassName); |
180 | $validateResult = new ValidateResult( |
181 | false, |
182 | ArrayPath::toString($nestValidator->arrayPath->getPaths()), |
183 | $mes |
184 | ); |
185 | $this->addValidateResults($validateResult); |
186 | continue; |
187 | } |
188 | |
189 | if ($object === []) { |
190 | // 値が空配列だったとき(ネストしたobject形式の配列を期待しているため、keyは必ず存在する) |
191 | $format = 'Invalid value. Expect "%s" schema for array type'; |
192 | $mes = \sprintf($format, $nestInstance->schemaClassName); |
193 | $validateResult = new ValidateResult( |
194 | false, |
195 | ArrayPath::toString($this->arrayPath->getPaths()), |
196 | $mes |
197 | ); |
198 | $this->addValidateResults($validateResult); |
199 | continue; |
200 | } |
201 | |
202 | $nestValidatorResult = $nestValidator->execute($object); |
203 | |
204 | if ($nestValidatorResult->failure()) { |
205 | $this->addValidateResults(...$nestValidatorResult->getValidateResults()); |
206 | } |
207 | } |
208 | $nestValidator->arrayPath->up(); |
209 | continue; |
210 | } |
211 | } |
212 | $this->arrayPath->up(); |
213 | } |
214 | } |