mirror of
https://github.com/Respect/Validation.git
synced 2026-03-16 15:25:45 +01:00
Currently, the templates that a user provides when running `assert()` can significantly impact how the message is displayed. Because of this, the formatters become complex as they all need to handle similar conditions to format results. This commit changes this behaviour, letting only the `InterpolationRenderer` handle the templates. This makes the code simpler and allows people to use the `InterpolationRenderer` directly, without needing to figure out how to handle templates. Thinking about it further, I believe handling templates is a concern for the `Renderer` anyway, and this will open the way to other improvements using the renderer. I also removed the exception that is thrown when the template is not a string, because I think that after validation has failed, we should not throw any other exceptions, as that could cause unexpected errors for users.
90 lines
2.6 KiB
PHP
90 lines
2.6 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Respect\Validation\Message\Formatter;
|
|
|
|
use Respect\Validation\Message\Renderer;
|
|
use Respect\Validation\Message\StringFormatter;
|
|
use Respect\Validation\Result;
|
|
|
|
use function array_filter;
|
|
use function array_reduce;
|
|
use function count;
|
|
use function rtrim;
|
|
use function sprintf;
|
|
use function str_repeat;
|
|
|
|
use const PHP_EOL;
|
|
|
|
final readonly class NestedListStringFormatter implements StringFormatter
|
|
{
|
|
/** @param array<string|int, mixed> $templates */
|
|
public function format(Result $result, Renderer $renderer, array $templates): string
|
|
{
|
|
return $this->formatRecursively($result, $renderer, $templates, 0);
|
|
}
|
|
|
|
/** @param array<string|int, mixed> $templates */
|
|
private function formatRecursively(
|
|
Result $result,
|
|
Renderer $renderer,
|
|
array $templates,
|
|
int $depth,
|
|
Result ...$siblings,
|
|
): string {
|
|
$formatted = '';
|
|
$displayedName = null;
|
|
if ($this->isVisible($result, ...$siblings)) {
|
|
$indentation = str_repeat(' ', $depth * 2);
|
|
$displayedName = $result->name;
|
|
$formatted .= sprintf('%s- %s' . PHP_EOL, $indentation, $renderer->render($result, $templates));
|
|
$depth++;
|
|
}
|
|
|
|
foreach ($result->children as $child) {
|
|
$formatted .= $this->formatRecursively(
|
|
$displayedName === $child->name ? $child->withoutName() : $child,
|
|
$renderer,
|
|
$templates,
|
|
$depth,
|
|
...array_filter($result->children, static fn(Result $sibling) => $sibling !== $child),
|
|
);
|
|
$formatted .= PHP_EOL;
|
|
}
|
|
|
|
return rtrim($formatted, PHP_EOL);
|
|
}
|
|
|
|
private function isVisible(Result $result, Result ...$siblings): bool
|
|
{
|
|
if ($result->hasCustomTemplate()) {
|
|
return true;
|
|
}
|
|
|
|
// Parents of an only child are not visible by default
|
|
if (count($result->children) !== 1) {
|
|
return true;
|
|
}
|
|
|
|
// Only children are always visible
|
|
if (count($siblings) === 0) {
|
|
return false;
|
|
}
|
|
|
|
// 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,
|
|
);
|
|
}
|
|
}
|