Fix isVisible() initial value bug in NestedListStringFormatter

The array_reduce in isVisible() used `true` as its initial value with
an `||` reducer, meaning it always returned `true` when siblings existed
regardless of their actual visibility. This caused unnecessary nesting
in full messages by showing single-child wrapper nodes that should have
been collapsed.

Replace array_reduce with a foreach loop starting from `false`, which
also enables early return on the first visible sibling.
This commit is contained in:
Alexandre Gomes Gaigalas 2026-03-05 16:03:49 -03:00
commit 33927e26d1
No known key found for this signature in database
GPG key ID: C68060CCE0AE33F0
3 changed files with 13 additions and 17 deletions

View file

@ -17,7 +17,6 @@ use Respect\Validation\Name;
use Respect\Validation\Result;
use function array_filter;
use function array_reduce;
use function count;
use function rtrim;
use function sprintf;
@ -89,13 +88,14 @@ final readonly class NestedListStringFormatter implements StringFormatter
}
// The visibility of a result then will depend on whether any of its siblings is visible
return array_reduce(
$siblings,
fn(bool $carry, Result $currentSibling) => $carry || $this->isVisible(
$currentSibling,
...array_filter($siblings, static fn(Result $sibling) => $sibling !== $currentSibling),
),
true,
);
foreach ($siblings as $key => $currentSibling) {
$otherSiblings = $siblings;
unset($otherSiblings[$key]);
if ($this->isVisible($currentSibling, ...$otherSiblings)) {
return true;
}
}
return false;
}
}

View file

@ -43,9 +43,7 @@ test('Scenario #1', catchFullMessage(
]))),
fn(string $fullMessage) => expect($fullMessage)->toBe(<<<'FULL_MESSAGE'
- the given data must pass all the rules
- `.mysql` must pass the rules
- `.mysql.host` must be a string
- `.postgresql` must pass the rules
- `.postgresql.user` must be a string
- `.mysql.host` must be a string
- `.postgresql.user` must be a string
FULL_MESSAGE),
));

View file

@ -46,10 +46,8 @@ test('https://github.com/Respect/Validation/issues/796', catchAll(
->and($message)->toBe('`.mysql.host` (<- the given data) must be a string')
->and($fullMessage)->toBe(<<<'FULL_MESSAGE'
- the given data must pass all the rules
- `.mysql` must pass the rules
- `.mysql.host` must be a string
- `.postgresql` must pass the rules
- `.postgresql.user` must be a string
- `.mysql.host` must be a string
- `.postgresql.user` must be a string
FULL_MESSAGE)
->and($messages)->toBe([
'__root__' => 'the given data must pass all the rules',