mirror of
https://github.com/Respect/Validation.git
synced 2026-03-17 07:45:45 +01:00
I've noticed that the `StandardFormatter` was quite bloated, which made it difficult to maintain. Understanding what each method was doing was quite complicated. Besides, the name "Standard" doesn't mean anything, because it doesn't say what the implementation does. I split the `Formatter` into two different interfaces: `StringFormatter` and `ArrayFormatter`, and I moved some code around: * `StandardFormatter::main()` -> `FirstResultStringFormatter` * `StandardFormatter::full()` -> `NestedListStringFormatter` * `StandardFormatter::array()` -> `NestedArrayFormatter` That opens up new ways of handling error messages, potentially introducing features like `JsonStringFormatter` or `FlatArrayFormatter` in the future. While working on this, I removed a significant amount of unnecessary code, which also improved my overall understanding of those formatters. I'm not very happy with all the methods in `ValidatorDefaults`, but I will refactor that later.
116 lines
3.4 KiB
PHP
116 lines
3.4 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\Message\Translator;
|
|
use Respect\Validation\Result;
|
|
|
|
use function array_filter;
|
|
use function array_map;
|
|
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
|
|
{
|
|
use PathProcessor;
|
|
|
|
public function __construct(
|
|
private Renderer $renderer,
|
|
private TemplateResolver $templateResolver,
|
|
) {
|
|
}
|
|
|
|
/** @param array<string|int, mixed> $templates */
|
|
public function format(Result $result, array $templates, Translator $translator): string
|
|
{
|
|
return $this->formatRecursively($result, $templates, $translator, 0);
|
|
}
|
|
|
|
/** @param array<string|int, mixed> $templates */
|
|
private function formatRecursively(
|
|
Result $result,
|
|
array $templates,
|
|
Translator $translator,
|
|
int $depth = 0,
|
|
Result ...$siblings,
|
|
): string {
|
|
$matchedTemplates = $this->templateResolver->selectMatches($result, $templates);
|
|
|
|
$formatted = '';
|
|
if ($this->isVisible($result, ...$siblings)) {
|
|
$indentation = str_repeat(' ', $depth * 2);
|
|
$formatted .= sprintf(
|
|
'%s- %s' . PHP_EOL,
|
|
$indentation,
|
|
$this->renderer->render(
|
|
$this->templateResolver->resolve(
|
|
$depth > 0 ? $result->withDeepestPath() : $result,
|
|
$matchedTemplates,
|
|
),
|
|
$translator,
|
|
),
|
|
);
|
|
$depth++;
|
|
}
|
|
|
|
if (!$this->templateResolver->hasMatch($result, $matchedTemplates)) {
|
|
$children = array_map(
|
|
fn(Result $child) => $this->overwritePath($result, $child),
|
|
$result->children,
|
|
);
|
|
foreach ($children as $child) {
|
|
$formatted .= $this->formatRecursively(
|
|
$child,
|
|
$matchedTemplates,
|
|
$translator,
|
|
$depth,
|
|
...array_filter($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,
|
|
);
|
|
}
|
|
}
|