respect-validation/library/Result.php
Henrique Moody 94ddfcd0bd
Create named constructor to create Result
The constructor of `Result` has many arguments, but that's not the
primary reason why I'm making this change. I want to change the
constructor, and it will become more complicated, so having this named
constructor will be useful in the next refactoring.

With this change, I also made the `id` mandatory. That made the
constructor look neater and most to promote almost all properties to the
constructor.

Another change was removing the `fromAdjacent` method, which was quite
confusing. I created the `asAdjacentOf` method, which is a bit clearer.
If anything, it makes all static methods named constructors. It will be
a bit more verbose, but more intuitive.
2025-12-26 22:34:43 +01:00

249 lines
7.1 KiB
PHP

<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation;
use Respect\Validation\Rules\Core\Nameable;
use function array_filter;
use function array_map;
use function count;
use function preg_match;
final readonly class Result
{
/** @var array<Result> */
public array $children;
/** @param array<string, mixed> $parameters */
public function __construct(
public bool $hasPassed,
public mixed $input,
public Rule $rule,
public Id $id,
public array $parameters = [],
public string $template = Rule::TEMPLATE_STANDARD,
public bool $hasInvertedMode = false,
public Name|null $name = null,
public Result|null $adjacent = null,
public Path|null $path = null,
Result ...$children,
) {
$this->children = $children;
}
/** @param array<string, mixed> $parameters */
public static function of(
bool $hasPassed,
mixed $input,
Rule $rule,
array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD,
): self {
return new self($hasPassed, $input, $rule, Id::fromRule($rule), $parameters, $template);
}
/** @param array<string, mixed> $parameters */
public static function failed(
mixed $input,
Rule $rule,
array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD,
): self {
return self::of(false, $input, $rule, $parameters, $template);
}
/** @param array<string, mixed> $parameters */
public static function passed(
mixed $input,
Rule $rule,
array $parameters = [],
string $template = Rule::TEMPLATE_STANDARD,
): self {
return self::of(true, $input, $rule, $parameters, $template);
}
public function asAdjacentOf(Result $result, string $prefix): Result
{
if ($this->allowsAdjacent()) {
return clone ($result, [
'id' => $this->id->withPrefix($prefix),
'adjacent' => $this->withInput($result->input),
]);
}
return clone ($this, [
'input' => $result->input,
'children' => array_map(
static fn(Result $child) => $child->asAdjacentOf($result, $prefix),
$this->children,
),
]);
}
public function withTemplate(string $template): self
{
return clone($this, ['template' => $template]);
}
/** @param array<string, mixed> $parameters */
public function withExtraParameters(array $parameters): self
{
// phpcs:ignore SlevomatCodingStandard.PHP.UselessParentheses
return clone($this, ['parameters' => $parameters + $this->parameters]);
}
public function withId(Id $id): self
{
return clone($this, ['id' => $id]);
}
public function withIdFrom(Rule $rule): self
{
return clone($this, ['id' => Id::fromRule($rule)]);
}
public function withPath(Path $path): self
{
if ($this->path === $path) {
return $this;
}
if ($this->path !== null) {
$this->path->parent = $path;
return $this;
}
return clone($this, [
'path' => $path,
'adjacent' => $this->adjacent?->withPath($path),
'children' => array_map(
static fn(Result $child) => $child->withPath($path),
$this->children,
),
]);
}
public function withoutName(): self
{
if ($this->name === null) {
return $this;
}
return clone ($this, [
'name' => null,
'adjacent' => $this->adjacent?->withoutName(),
'children' => array_map(
fn(Result $child) => $child->name === $this->name ? $child->withoutName() : $child,
$this->children,
),
]);
}
public function withChildren(Result ...$children): self
{
if ($this->path === null) {
return clone($this, ['children' => $children]);
}
return clone($this, ['children' => array_map(fn(Result $child) => $child->withPath($this->path), $children)]);
}
public function withName(Name $name): self
{
if ($this->path !== null && $this->name?->path !== $this->path) {
$name = $name->withPath($this->path);
}
return clone($this, [
'name' => $this->name ?? $name,
'adjacent' => $this->adjacent?->withName($name),
'children' => array_map(
static fn(Result $child) => $child->path === null ? $child->withName($child->name ?? $name) : $child,
$this->children,
),
]);
}
public function withNameFrom(Rule $rule): self
{
if ($rule instanceof Nameable && $rule->getName() !== null) {
return clone($this, [
'name' => $this->name ?? $rule->getName(),
'adjacent' => $this->adjacent?->withNameFrom($rule),
'children' => array_map(
static fn(Result $child) => $child->withNameFrom($rule),
$this->children,
),
]);
}
return $this;
}
public function withInput(mixed $input): self
{
$currentInput = $this->input;
return clone($this, [
'input' => $input,
'children' => array_map(
static fn(Result $child) => $child->input === $currentInput ? $child->withInput($input) : $child,
$this->children,
),
]);
}
public function withAdjacent(Result $adjacent): self
{
return clone($this, ['adjacent' => $adjacent]);
}
public function withToggledValidation(): self
{
return clone($this, [
'hasPassed' => !$this->hasPassed,
'adjacent' => $this->adjacent?->withToggledValidation(),
'children' => array_map(static fn(Result $child) => $child->withToggledValidation(), $this->children),
]);
}
public function withToggledModeAndValidation(): self
{
return clone($this, [
'hasPassed' => !$this->hasPassed,
'hasInvertedMode' => !$this->hasInvertedMode,
'adjacent' => $this->adjacent?->withToggledModeAndValidation(),
'children' => array_map(
static fn(Result $child) => $child->withToggledModeAndValidation(),
$this->children,
),
]);
}
public function hasCustomTemplate(): bool
{
return preg_match('/__[0-9a-z_]+_/', $this->template) === 0;
}
public function allowsAdjacent(): bool
{
if ($this->children === [] && !$this->hasCustomTemplate()) {
return true;
}
$childrenThatAllowAdjacent = array_filter(
$this->children,
static fn(Result $child) => $child->allowsAdjacent(),
);
return count($childrenThatAllowAdjacent) === 1;
}
}