mirror of
https://github.com/Respect/Validation.git
synced 2026-03-16 15:25:45 +01:00
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.
249 lines
7.1 KiB
PHP
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;
|
|
}
|
|
}
|