|
13 | 13 | use Respect\Validation\Message\Renderer; |
14 | 14 | use Respect\Validation\Result; |
15 | 15 |
|
| 16 | +use function array_reduce; |
16 | 17 | use function count; |
17 | 18 | use function current; |
| 19 | +use function is_array; |
| 20 | +use function is_numeric; |
18 | 21 |
|
19 | 22 | final readonly class NestedArrayFormatter implements ArrayFormatter |
20 | 23 | { |
|
25 | 28 | */ |
26 | 29 | public function format(Result $result, Renderer $renderer, array $templates): array |
27 | 30 | { |
28 | | - if (count($result->children) === 0) { |
29 | | - return [ |
30 | | - $result->path->value ?? $result->id->value => $renderer->render($result, $templates), |
31 | | - ]; |
| 31 | + if ($result->children === []) { |
| 32 | + return [$this->getKey($result) => $renderer->render($result, $templates)]; |
32 | 33 | } |
33 | 34 |
|
34 | | - $messages = []; |
35 | | - foreach ($result->children as $child) { |
36 | | - $key = $child->path->value ?? $child->id->value; |
37 | | - $messages[$key] = $this->format( |
38 | | - $child->withoutName(), |
| 35 | + [$children, $hasString] = $this->prepareChildren($result->children); |
| 36 | + |
| 37 | + $messages = array_reduce( |
| 38 | + $children, |
| 39 | + fn(array $messages, array $item) => $this->appendMessage( |
| 40 | + $messages, |
| 41 | + $item['key'], |
| 42 | + $item['child'], |
39 | 43 | $renderer, |
40 | 44 | $templates, |
41 | | - ); |
42 | | - if (count($messages[$key]) !== 1) { |
43 | | - continue; |
| 45 | + $hasString, |
| 46 | + ), |
| 47 | + [], |
| 48 | + ); |
| 49 | + |
| 50 | + if (count($messages) > 1) { |
| 51 | + return ['__root__' => $renderer->render($result, $templates)] + $messages; |
| 52 | + } |
| 53 | + |
| 54 | + return $messages; |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * @param array<Result> $children |
| 59 | + * |
| 60 | + * @return array{0: array<array{key: string|int, child: Result}>, 1: bool} |
| 61 | + */ |
| 62 | + private function prepareChildren(array $children): array |
| 63 | + { |
| 64 | + $mapped = []; |
| 65 | + $hasString = false; |
| 66 | + |
| 67 | + foreach ($children as $child) { |
| 68 | + $key = $this->getKey($child); |
| 69 | + if (!is_numeric($key)) { |
| 70 | + $hasString = true; |
44 | 71 | } |
45 | 72 |
|
46 | | - $messages[$key] = current($messages[$key]); |
| 73 | + $mapped[] = ['key' => $key, 'child' => $child]; |
47 | 74 | } |
48 | 75 |
|
49 | | - if (count($messages) > 1) { |
50 | | - return ['__root__' => $renderer->render($result, $templates)] + $messages; |
| 76 | + return [$mapped, $hasString]; |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + * @param array<string|int, mixed> $messages |
| 81 | + * @param array<string|int, mixed> $templates |
| 82 | + * |
| 83 | + * @return array<string|int, mixed> |
| 84 | + */ |
| 85 | + private function appendMessage( |
| 86 | + array $messages, |
| 87 | + string|int $key, |
| 88 | + Result $child, |
| 89 | + Renderer $renderer, |
| 90 | + array $templates, |
| 91 | + bool $hasString, |
| 92 | + ): array { |
| 93 | + if ($hasString && is_numeric($key)) { |
| 94 | + $key = $child->id->value; |
51 | 95 | } |
52 | 96 |
|
| 97 | + $message = $this->renderChild($child, $renderer, $templates); |
| 98 | + |
| 99 | + if (!$hasString) { |
| 100 | + $messages[] = $message; |
| 101 | + |
| 102 | + return $messages; |
| 103 | + } |
| 104 | + |
| 105 | + if (isset($messages[$key])) { |
| 106 | + if (!is_array($messages[$key])) { |
| 107 | + $messages[$key] = [$messages[$key]]; |
| 108 | + } |
| 109 | + |
| 110 | + $messages[$key][] = $message; |
| 111 | + |
| 112 | + return $messages; |
| 113 | + } |
| 114 | + |
| 115 | + $messages[$key] = $message; |
| 116 | + |
53 | 117 | return $messages; |
54 | 118 | } |
| 119 | + |
| 120 | + /** |
| 121 | + * @param array<string|int, array<string>> $templates |
| 122 | + * |
| 123 | + * @return array<string>|string |
| 124 | + */ |
| 125 | + private function renderChild(Result $child, Renderer $renderer, array $templates): array|string |
| 126 | + { |
| 127 | + $formatted = $this->format( |
| 128 | + $child->withoutName(), |
| 129 | + $renderer, |
| 130 | + $templates, |
| 131 | + ); |
| 132 | + |
| 133 | + if (count($formatted) === 1) { |
| 134 | + return current($formatted); |
| 135 | + } |
| 136 | + |
| 137 | + return $formatted; |
| 138 | + } |
| 139 | + |
| 140 | + private function getKey(Result $result): string|int |
| 141 | + { |
| 142 | + return $result->path->value ?? $result->id->value; |
| 143 | + } |
55 | 144 | } |
0 commit comments