Skip to content

Commit 399be98

Browse files
committed
WIP: Improve messages in an array format
- Name: That's what is in the placeholder, and could be used for path and id as well - Id: Tha could be the name of the rule, just a means to identify it and be able to build templates for it on that level. - Path: That's the real path of the rule, which will be useful when dealing with arrays or objects.
1 parent aa293de commit 399be98

25 files changed

+219
-136
lines changed

library/Message/StandardFormatter.php

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
use function array_filter;
1616
use function array_key_exists;
17+
use function array_merge;
1718
use function array_reduce;
1819
use function array_values;
1920
use function count;
2021
use function current;
2122
use function is_array;
2223
use function is_string;
24+
use function key;
2325
use function Respect\Stringifier\stringify;
2426
use function rtrim;
2527
use function sprintf;
@@ -97,26 +99,45 @@ public function full(
9799
*/
98100
public function array(Result $result, array $templates, Translator $translator): array
99101
{
102+
$whoamid = sprintf('%s.%s.%s', $result->rule::class, $result->id, $result->name);
100103
$selectedTemplates = $this->selectTemplates($result, $templates);
101104
$deduplicatedChildren = $this->extractDeduplicatedChildren($result);
102105
if (count($deduplicatedChildren) === 0 || $this->isFinalTemplate($result, $selectedTemplates)) {
103106
return [
104-
$result->id => $this->renderer->render($this->getTemplated($result, $selectedTemplates), $translator),
107+
$result->path ?? $result->id => $this->renderer->render(
108+
$this->getTemplated($result, $selectedTemplates),
109+
$translator
110+
),
105111
];
106112
}
107113

108114
$messages = [];
109115
foreach ($deduplicatedChildren as $child) {
110-
$messages[$child->id] = $this->array(
116+
$childKey = $child->path ?? $child->id;
117+
118+
$messages[$childKey] = $this->array(
111119
$child,
112120
$this->selectTemplates($child, $selectedTemplates),
113121
$translator
114122
);
115-
if (count($messages[$child->id]) !== 1) {
123+
124+
if ($childKey == 'each' && is_array($messages['each'])) {
125+
$messages = array_merge($messages, $messages['each']);
126+
unset($messages['each']);
127+
continue;
128+
}
129+
130+
if (count($messages[$childKey]) !== 1) {
116131
continue;
117132
}
118133

119-
$messages[$child->id] = current($messages[$child->id]);
134+
$grantChildKey = key($messages[$childKey]);
135+
if ($grantChildKey != $childKey) {
136+
continue;
137+
}
138+
139+
140+
$messages[$grantChildKey] = current($messages[$grantChildKey]);
120141
}
121142

122143
if (count($messages) > 1) {
@@ -172,21 +193,27 @@ private function getTemplated(Result $result, array $templates): Result
172193
return $result;
173194
}
174195

175-
if (!isset($templates[$result->id]) && isset($templates['__root__'])) {
176-
return $result->withTemplate($templates['__root__']);
196+
$keys = [$result->name, $result->path, $result->id];
197+
foreach ($keys as $key) {
198+
if (isset($templates[$key]) && is_string($templates[$key])) {
199+
return $result->withTemplate($templates[$key]);
200+
}
177201
}
178202

179-
if (!isset($templates[$result->id])) {
180-
return $result;
203+
if (isset($templates['__root__'])) {
204+
return $result->withTemplate($templates['__root__']);
181205
}
182206

183-
$template = $templates[$result->id];
184-
if (is_string($template)) {
185-
return $result->withTemplate($template);
207+
if (!isset($templates[$result->id]) && !isset($templates[$result->path]) && !isset($templates[$result->name])) {
208+
return $result;
186209
}
187210

188211
throw new ComponentException(
189-
sprintf('Template for "%s" must be a string, %s given', $result->id, stringify($template))
212+
sprintf(
213+
'Template for "%s" must be a string, %s given',
214+
$result->path ?? $result->name ?? $result->id,
215+
stringify($templates)
216+
)
190217
);
191218
}
192219

@@ -195,26 +222,37 @@ private function getTemplated(Result $result, array $templates): Result
195222
*/
196223
private function isFinalTemplate(Result $result, array $templates): bool
197224
{
198-
if (isset($templates[$result->id]) && is_string($templates[$result->id])) {
199-
return true;
225+
$keys = [$result->name, $result->path, $result->id];
226+
foreach ($keys as $key) {
227+
if (isset($templates[$key]) && is_string($templates[$key])) {
228+
return true;
229+
}
200230
}
201231

202232
if (count($templates) !== 1) {
203233
return false;
204234
}
205235

206-
return isset($templates['__root__']) || isset($templates[$result->id]);
236+
foreach ($keys as $key) {
237+
if (isset($templates[$key])) {
238+
return true;
239+
}
240+
}
241+
242+
return isset($templates['__root__']);
207243
}
208244

209245
/**
210246
* @param array<string, mixed> $templates
211247
*
212248
* @return array<string, mixed>
213249
*/
214-
private function selectTemplates(Result $message, array $templates): array
250+
private function selectTemplates(Result $result, array $templates): array
215251
{
216-
if (isset($templates[$message->id]) && is_array($templates[$message->id])) {
217-
return $templates[$message->id];
252+
foreach ([$result->name, $result->path, $result->id] as $key) {
253+
if (isset($templates[$key]) && is_array($templates[$key])) {
254+
return $templates[$key];
255+
}
218256
}
219257

220258
return $templates;
@@ -227,7 +265,7 @@ private function extractDeduplicatedChildren(Result $result): array
227265
$deduplicatedResults = [];
228266
$duplicateCounters = [];
229267
foreach ($result->children as $child) {
230-
$id = $child->id;
268+
$id = $child->path ?? $child->id;
231269
if (isset($duplicateCounters[$id])) {
232270
$id .= '.' . ++$duplicateCounters[$id];
233271
} elseif (array_key_exists($id, $deduplicatedResults)) {
@@ -236,7 +274,7 @@ private function extractDeduplicatedChildren(Result $result): array
236274
$duplicateCounters[$id] = 2;
237275
$id .= '.2';
238276
}
239-
$deduplicatedResults[$id] = $child->isValid ? null : $child->withId($id);
277+
$deduplicatedResults[$id] = $child->isValid ? null : $child->withId((string) $id);
240278
}
241279

242280
return array_values(array_filter($deduplicatedResults));

library/Result.php

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ public function __construct(
3939
public readonly Mode $mode = Mode::DEFAULT,
4040
?string $name = null,
4141
?string $id = null,
42+
public readonly string|int|null $path = null,
4243
public readonly ?Result $subsequent = null,
43-
public readonly bool $unchangeableId = false,
4444
Result ...$children,
4545
) {
4646
$this->name = $rule->getName() ?? $name;
@@ -76,21 +76,17 @@ public function withTemplate(string $template): self
7676

7777
public function withId(string $id): self
7878
{
79-
if ($this->unchangeableId) {
80-
return $this;
81-
}
82-
8379
return $this->clone(id: $id);
8480
}
8581

86-
public function withUnchangeableId(string $id): self
82+
public function withPath(string|int $path): self
8783
{
88-
return $this->clone(id: $id, unchangeableId: true);
84+
return $this->clone(path: $path);
8985
}
9086

91-
public function withPrefixedId(string $prefix): self
87+
public function withPrefix(string $prefix): self
9288
{
93-
if ($this->id === $this->name || $this->unchangeableId) {
89+
if ($this->id === $this->name || $this->path !== null) {
9490
return $this;
9591
}
9692

@@ -176,8 +172,8 @@ private function clone(
176172
?Mode $mode = null,
177173
?string $name = null,
178174
?string $id = null,
175+
string|int|null $path = null,
179176
?Result $subsequent = null,
180-
?bool $unchangeableId = null,
181177
?array $children = null
182178
): self {
183179
return new self(
@@ -189,8 +185,8 @@ private function clone(
189185
$mode ?? $this->mode,
190186
$name ?? $this->name,
191187
$id ?? $this->id,
188+
$path ?? $this->path,
192189
$subsequent ?? $this->subsequent,
193-
$unchangeableId ?? $this->unchangeableId,
194190
...($children ?? $this->children)
195191
);
196192
}

library/Rules/DateTimeDiff.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private function enrichResult(string $now, mixed $input, Result $result): Result
105105
$template = $now === 'now' ? self::TEMPLATE_STANDARD : self::TEMPLATE_CUSTOMIZED;
106106

107107
return (new Result($result->isValid, $input, $this, $parameters, $template, id: $result->id))
108-
->withPrefixedId('dateTimeDiff')
108+
->withPrefix('dateTimeDiff')
109109
->withSubsequent($result->withNameIfMissing($name));
110110
}
111111

library/Rules/Each.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Respect\Validation\Result;
1515
use Respect\Validation\Rules\Core\FilteredNonEmptyArray;
1616

17-
use function array_map;
1817
use function array_reduce;
1918

2019
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
@@ -27,7 +26,10 @@ final class Each extends FilteredNonEmptyArray
2726
/** @param non-empty-array<mixed> $input */
2827
protected function evaluateNonEmptyArray(array $input): Result
2928
{
30-
$children = array_map(fn ($item) => $this->rule->evaluate($item), $input);
29+
$children = [];
30+
foreach ($input as $key => $value) {
31+
$children[] = $this->rule->evaluate($value)->withPath($key);
32+
}
3133
$isValid = array_reduce($children, static fn ($carry, $childResult) => $carry && $childResult->isValid, true);
3234
if ($isValid) {
3335
return Result::passed($input, $this)->withChildren(...$children);

library/Rules/Key.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function evaluate(mixed $input): Result
4141

4242
return $this->rule
4343
->evaluate($input[$this->key])
44-
->withUnchangeableId((string) $this->key)
44+
->withPath($this->key)
4545
->withNameIfMissing($this->rule->getName() ?? (string) $this->key);
4646
}
4747
}

library/Rules/KeyOptional.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function evaluate(mixed $input): Result
4141

4242
return $this->rule
4343
->evaluate($input[$this->key])
44-
->withUnchangeableId((string) $this->key)
44+
->withPath($this->key)
4545
->withNameIfMissing($this->rule->getName() ?? (string) $this->key);
4646
}
4747
}

library/Rules/Length.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private function enrichResult(mixed $input, Result $result): Result
5959
}
6060

6161
return (new Result($result->isValid, $input, $this, id: $result->id))
62-
->withPrefixedId('length')
62+
->withPrefix('length')
6363
->withSubsequent($result->withInput($input));
6464
}
6565

library/Rules/Max.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@ final class Max extends FilteredNonEmptyArray
2626
/** @param non-empty-array<mixed> $input */
2727
protected function evaluateNonEmptyArray(array $input): Result
2828
{
29-
$result = $this->rule->evaluate(max($input))->withPrefixedId('max');
29+
$result = $this->rule->evaluate(max($input))->withPrefix('max');
3030
$template = $this->getName() === null ? self::TEMPLATE_STANDARD : self::TEMPLATE_NAMED;
3131

32-
return (new Result($result->isValid, $input, $this, [], $template, id: $result->id))->withSubsequent($result);
32+
return (new Result($result->isValid, $input, $this, [], $template, id: $result->id))
33+
->withSubsequent($result);
3334
}
3435
}

library/Rules/Min.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class Min extends FilteredNonEmptyArray
2626
/** @param non-empty-array<mixed> $input */
2727
protected function evaluateNonEmptyArray(array $input): Result
2828
{
29-
$result = $this->rule->evaluate(min($input))->withPrefixedId('min');
29+
$result = $this->rule->evaluate(min($input))->withPrefix('min');
3030
$template = $this->getName() === null ? self::TEMPLATE_STANDARD : self::TEMPLATE_NAMED;
3131

3232
return (new Result($result->isValid, $input, $this, [], $template, id: $result->id))->withSubsequent($result);

library/Rules/Not.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ final class Not extends Wrapper
1818
{
1919
public function evaluate(mixed $input): Result
2020
{
21-
return $this->rule->evaluate($input)->withInvertedMode()->withPrefixedId('not');
21+
return $this->rule->evaluate($input)->withInvertedMode()->withPrefix('not');
2222
}
2323
}

library/Rules/NullOr.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ private function enrichResult(Result $result): Result
4141
{
4242
if ($result->allowsSubsequent()) {
4343
return $result
44-
->withPrefixedId('nullOr')
44+
->withPrefix('nullOr')
4545
->withSubsequent(new Result($result->isValid, $result->input, $this));
4646
}
4747

library/Rules/Property.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function evaluate(mixed $input): Result
3838

3939
return $this->rule
4040
->evaluate($this->extractPropertyValue($input, $this->propertyName))
41-
->withUnchangeableId($this->propertyName)
41+
->withPath($this->propertyName)
4242
->withNameIfMissing($this->rule->getName() ?? $this->propertyName);
4343
}
4444
}

library/Rules/PropertyOptional.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function evaluate(mixed $input): Result
3838

3939
return $this->rule
4040
->evaluate($this->extractPropertyValue($input, $this->propertyName))
41-
->withUnchangeableId($this->propertyName)
41+
->withPath($this->propertyName)
4242
->withNameIfMissing($this->rule->getName() ?? $this->propertyName);
4343
}
4444
}

library/Rules/Size.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private function enrichResult(mixed $input, Result $result): Result
110110
$parameters = ['unit' => self::DATA_STORAGE_UNITS[$this->unit]['name']];
111111

112112
return (new Result($result->isValid, $input, $this, $parameters, id: $result->id))
113-
->withPrefixedId('size')
113+
->withPrefix('size')
114114
->withSubsequent($result->withInput($input));
115115
}
116116
}

library/Rules/UndefOr.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ private function enrichResult(Result $result): Result
4444
{
4545
if ($result->allowsSubsequent()) {
4646
return $result
47-
->withPrefixedId('undefOr')
47+
->withPrefix('undefOr')
4848
->withSubsequent(new Result($result->isValid, $result->input, $this));
4949
}
5050

tests/feature/Issues/Issue1033Test.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
FULL_MESSAGE,
1919
[
2020
'__root__' => 'Each item in `["A", "B", "B"]` must be valid',
21-
'equals.1' => '"A" must be equal to 1',
22-
'equals.2' => '"B" must be equal to 1',
23-
'equals.3' => '"B" must be equal to 1',
21+
0 => '"A" must be equal to 1',
22+
1 => '"B" must be equal to 1',
23+
2 => '"B" must be equal to 1',
2424
]
2525
));

tests/feature/Issues/Issue1289Test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
- description must be a string value
5555
FULL_MESSAGE,
5656
[
57-
'allOf' => [
57+
0 => [
5858
'__root__' => 'These rules must pass for `["default": 2, "description": [], "children": ["nope"]]`',
5959
'default' => [
6060
'__root__' => 'Only one of these rules must pass for default',

0 commit comments

Comments
 (0)