Skip to content

Commit

Permalink
Merge pull request #24 from awurth/asserter
Browse files Browse the repository at this point in the history
Add data collector
  • Loading branch information
awurth committed Mar 27, 2023
2 parents 3766776 + 34078be commit 0592ab7
Show file tree
Hide file tree
Showing 16 changed files with 515 additions and 89 deletions.
84 changes: 84 additions & 0 deletions src/Asserter.php
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Awurth Validator package.
*
* (c) Alexis Wurth <awurth.dev@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Awurth\Validator;

use Respect\Validation\Exceptions\NestedValidationException;

final class Asserter implements AsserterInterface
{
/**
* @param array<string, string> $messages
*/
public function __construct(
private readonly ValidationFailureCollectionFactoryInterface $validationFailureCollectionFactory,
private readonly ValidationFailureFactoryInterface $validationFailureFactory,
private readonly array $messages = [],
) {
}

/**
* @param array<string, string> $messages
*/
public static function create(array $messages = []): self
{
return new self(new ValidationFailureCollectionFactory(), new ValidationFailureFactory(), $messages);
}

public function assert(mixed $subject, ValidationInterface $validation, array $messages = []): ValidationFailureCollectionInterface
{
$failures = $this->validationFailureCollectionFactory->create();

try {
$validation->getRules()->assert($subject);
} catch (NestedValidationException $exception) {
if ($message = $validation->getMessage()) {
$failures->add(
$this->validationFailureFactory->create($message, $subject, $validation->getProperty())
);

return $failures;
}

$exceptionMessages = $this->extractMessagesFromException($exception, $validation, $messages);
foreach ($exceptionMessages as $ruleName => $message) {
$failures->add(
$this->validationFailureFactory->create($message, $subject, $validation->getProperty(), $ruleName)
);
}
}

return $failures;
}

/**
* @param array<string, string> $messages
*
* @return array<string, string>
*/
private function extractMessagesFromException(NestedValidationException $exception, ValidationInterface $validation, array $messages = []): array
{
$definedMessages = \array_replace($this->messages, $messages, $validation->getMessages());

$errors = [];
foreach ($exception->getMessages($definedMessages) as $name => $error) {
if (\is_array($error)) {
$errors = [...$errors, ...$error];
} else {
$errors[$name] = $error;
}
}

return $errors;
}
}
22 changes: 22 additions & 0 deletions src/AsserterInterface.php
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Awurth Validator package.
*
* (c) Alexis Wurth <awurth.dev@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Awurth\Validator;

interface AsserterInterface
{
/**
* @param array<string, string> $messages
*/
public function assert(mixed $subject, ValidationInterface $validation, array $messages = []): ValidationFailureCollectionInterface;
}
41 changes: 41 additions & 0 deletions src/DataCollectorAsserter.php
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Awurth Validator package.
*
* (c) Alexis Wurth <awurth.dev@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Awurth\Validator;

final class DataCollectorAsserter implements DataCollectorAsserterInterface
{
private readonly ValidatedValueCollectionInterface $data;

public function __construct(private readonly AsserterInterface $asserter)
{
$this->data = new ValidatedValueCollection();
}

public static function create(): self
{
return new self(Asserter::create());
}

public function assert(mixed $subject, ValidationInterface $validation, array $messages = []): ValidationFailureCollectionInterface
{
$this->data->add(new ValidatedValue($validation, $subject));

return $this->asserter->assert($subject, $validation, $messages);
}

public function getData(): ValidatedValueCollectionInterface
{
return $this->data;
}
}
19 changes: 19 additions & 0 deletions src/DataCollectorAsserterInterface.php
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Awurth Validator package.
*
* (c) Alexis Wurth <awurth.dev@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Awurth\Validator;

interface DataCollectorAsserterInterface extends AsserterInterface
{
public function getData(): ValidatedValueCollectionInterface;
}
10 changes: 4 additions & 6 deletions src/StatefulValidator.php
Expand Up @@ -26,13 +26,11 @@ public function __construct(
$this->failures = $this->validationFailureCollectionFactory->create();
}

public static function create(
?ValidationFailureCollectionFactoryInterface $validationFailureCollectionFactory = null,
?ValidatorInterface $validator = null,
): self {
public static function create(?AsserterInterface $asserter = null): self
{
return new self(
$validationFailureCollectionFactory ?? new ValidationFailureCollectionFactory(),
$validator ?? Validator::create(),
new ValidationFailureCollectionFactory(),
Validator::create($asserter),
);
}

Expand Down
39 changes: 32 additions & 7 deletions src/Twig/LegacyValidatorExtension.php
Expand Up @@ -13,6 +13,7 @@

namespace Awurth\Validator\Twig;

use Awurth\Validator\DataCollectorAsserterInterface;
use Awurth\Validator\StatefulValidatorInterface;
use Awurth\Validator\ValidationFailureInterface;
use Twig\Extension\AbstractExtension;
Expand All @@ -35,24 +36,37 @@ final class LegacyValidatorExtension extends AbstractExtension
/**
* @param string[] $functionNames An array of names for Twig functions
*/
public function __construct(private readonly StatefulValidatorInterface $validator, array $functionNames = [])
{
public function __construct(
private readonly StatefulValidatorInterface $validator,
private readonly ?DataCollectorAsserterInterface $asserter = null,
array $functionNames = [],
) {
$this->functionNames = [
'error' => $functionNames['error'] ?? 'error',
'errors' => $functionNames['errors'] ?? 'errors',
'has_error' => $functionNames['has_error'] ?? 'has_error',
'has_errors' => $functionNames['has_errors'] ?? 'has_errors',
];

if (null !== $asserter) {
$this->functionNames['val'] = $functionNames['val'] ?? 'val';
}
}

public function getFunctions(): array
{
return [
new TwigFunction($this->functionNames['error'], [$this, 'getError']),
new TwigFunction($this->functionNames['errors'], [$this, 'getErrors']),
new TwigFunction($this->functionNames['has_error'], [$this, 'hasError']),
new TwigFunction($this->functionNames['has_errors'], [$this, 'hasErrors']),
$functions = [
new TwigFunction($this->functionNames['error'], $this->getError(...)),
new TwigFunction($this->functionNames['errors'], $this->getErrors(...)),
new TwigFunction($this->functionNames['has_error'], $this->hasError(...)),
new TwigFunction($this->functionNames['has_errors'], $this->hasErrors(...)),
];

if (null !== $this->asserter) {
$functions[] = new TwigFunction($this->functionNames['val'], $this->getValue(...));
}

return $functions;
}

public function getError(string $key, int $index = 0): ?string
Expand All @@ -79,6 +93,17 @@ public function getErrors(?string $key = null): array
);
}

public function getValue(string $key): mixed
{
foreach ($this->asserter->getData() as $validatedValue) {
if ($validatedValue->getValidation()->getProperty() === $key) {
return $validatedValue->getValue();
}
}

return null;
}

public function hasError(string $key): bool
{
return null !== $this->validator->getFailures()->find(static fn (ValidationFailureInterface $failure) => $failure->getProperty() === $key);
Expand Down
37 changes: 31 additions & 6 deletions src/Twig/ValidatorExtension.php
Expand Up @@ -13,6 +13,7 @@

namespace Awurth\Validator\Twig;

use Awurth\Validator\DataCollectorAsserterInterface;
use Awurth\Validator\StatefulValidatorInterface;
use Awurth\Validator\ValidationFailureCollectionInterface;
use Awurth\Validator\ValidationFailureInterface;
Expand All @@ -36,22 +37,35 @@ final class ValidatorExtension extends AbstractExtension
/**
* @param string[] $functionNames An array of names for Twig functions
*/
public function __construct(private readonly StatefulValidatorInterface $validator, array $functionNames = [])
{
public function __construct(
private readonly StatefulValidatorInterface $validator,
private readonly ?DataCollectorAsserterInterface $asserter = null,
array $functionNames = [],
) {
$this->functionNames = [
'error' => $functionNames['error'] ?? 'error',
'errors' => $functionNames['errors'] ?? 'errors',
'has_errors' => $functionNames['has_errors'] ?? 'has_errors',
];

if (null !== $asserter) {
$this->functionNames['val'] = $functionNames['val'] ?? 'val';
}
}

public function getFunctions(): array
{
return [
new TwigFunction($this->functionNames['error'], [$this, 'findFirst']),
new TwigFunction($this->functionNames['errors'], [$this, 'findErrors']),
new TwigFunction($this->functionNames['has_errors'], [$this, 'hasErrors']),
$functions = [
new TwigFunction($this->functionNames['error'], $this->findFirst(...)),
new TwigFunction($this->functionNames['errors'], $this->findErrors(...)),
new TwigFunction($this->functionNames['has_errors'], $this->hasErrors(...)),
];

if (null !== $this->asserter) {
$functions[] = new TwigFunction($this->functionNames['val'], $this->findValue(...));
}

return $functions;
}

public function findFirst(?callable $callback = null): ?ValidationFailureInterface
Expand All @@ -76,4 +90,15 @@ public function hasErrors(): bool
{
return 0 !== $this->validator->getFailures()->count();
}

public function findValue(callable $callback): mixed
{
foreach ($this->asserter->getData() as $index => $validatedValue) {
if ($callback($validatedValue, $index)) {
return $validatedValue->getValue();
}
}

return null;
}
}
31 changes: 31 additions & 0 deletions src/ValidatedValue.php
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Awurth Validator package.
*
* (c) Alexis Wurth <awurth.dev@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Awurth\Validator;

final class ValidatedValue implements ValidatedValueInterface
{
public function __construct(private readonly ValidationInterface $validation, private readonly mixed $value)
{
}

public function getValidation(): ValidationInterface
{
return $this->validation;
}

public function getValue(): mixed
{
return $this->value;
}
}

0 comments on commit 0592ab7

Please sign in to comment.