respect-validation/library/ResultQuery.php
Henrique Moody 16c2a75d00
Enable validating inputs without throwing exceptions
Currently, the only way to handle when a validation fails is by
`assert()`, which either throws an exception or doesn't. That means that
every time a user wants to handle the results, they must use try-catch
blocks, which may add some overhead.

This commit introduces the `ResultQuery` class that wraps a `Result`
object, providing an alternative to exception-based validation. This
allows users to handle results directly without try-catch blocks.

I’m taking a risky move here using the old method `validate()`, but I
can’t think of a better name for this method.
2025-12-22 20:37:11 +01:00

133 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
namespace Respect\Validation;
use Respect\Validation\Message\ArrayFormatter;
use Respect\Validation\Message\Renderer;
use Respect\Validation\Message\StringFormatter;
use Stringable;
use function array_shift;
use function explode;
use function implode;
use function is_string;
final readonly class ResultQuery implements Stringable
{
/** @param array<string|int, mixed> $templates */
public function __construct(
private Result $result,
private Renderer $renderer,
private StringFormatter $messageFormatter,
private StringFormatter $fullMessageFormatter,
private ArrayFormatter $messagesFormatter,
private array $templates,
) {
}
public function findById(string $id): self|null
{
if ($this->result->id->value === $id) {
return $this;
}
foreach ($this->result->children as $child) {
$resultQuery = clone ($this, ['result' => $child]);
if ($child->id->value === $id) {
return $resultQuery;
}
return $resultQuery->findById($id);
}
return null;
}
public function findByName(string $name): self|null
{
if ($this->result->name?->value === $name) {
return $this;
}
foreach ($this->result->children as $child) {
$resultQuery = clone ($this, ['result' => $child]);
if ($child->name?->value === $name) {
return $resultQuery;
}
return $resultQuery->findByName($name);
}
return null;
}
public function findByPath(string|int $path): self|null
{
if ($this->result->path?->value === $path) {
return $this;
}
$paths = is_string($path) ? explode('.', $path) : [$path];
$currentPath = array_shift($paths);
foreach ($this->result->children as $child) {
if ($child->path?->value !== $currentPath) {
continue;
}
$resultQuery = clone ($this, ['result' => $child]);
if ($paths === []) {
return $resultQuery;
}
return $resultQuery->findByPath(is_string($path) ? implode('.', $paths) : $path);
}
return null;
}
public function isValid(): bool
{
return $this->result->hasPassed;
}
public function toMessage(): string
{
if ($this->result->hasPassed) {
return '';
}
return $this->messageFormatter->format($this->result, $this->renderer, $this->templates);
}
public function toFullMessage(): string
{
if ($this->result->hasPassed) {
return '';
}
return $this->fullMessageFormatter->format($this->result, $this->renderer, $this->templates);
}
/** @return array<string|int, mixed> */
public function toArrayMessages(): array
{
if ($this->result->hasPassed) {
return [];
}
return $this->messagesFormatter->format($this->result, $this->renderer, $this->templates);
}
public function __toString(): string
{
return $this->toMessage();
}
}