Skip to content

Commit

Permalink
Merge pull request #2 from alleyinteractive/feature/supporting-valida…
Browse files Browse the repository at this point in the history
…tors

Add `Not` and `AnyValidator` validators
  • Loading branch information
dlh01 authored Aug 23, 2022
2 parents a5149c2 + 1f58f5f commit e60c333
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 4 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

This library adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/en/1.0.0/).

## 1.1.0

### Added

- `Not` and `AnyValidator` validators.

### Fixed

- Incorrect value being passed to `BaseValidator::testValue()` in `BaseValidator::isValid()`.

## 1.0.0

Initial release.
41 changes: 38 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ class Float extends \Alley\Validator\BaseValidator
}
```

## "Any Validator" Chains

`\Alley\Validator\AnyValidator` is like a [validator chain](https://docs.laminas.dev/laminas-validator/validator-chains/) except that it connects the validators with "OR," marking input as valid as soon it passes one of the given validators.

### Basic usage

```php
<?php

$valid = new \Alley\Validator\AnyValidator([new \Laminas\Validator\LessThan(['max' => 10])]);
$valid->attach(new \Laminas\Validator\GreaterThan(['min' => 90]));

$valid->isValid(9); // true
$valid->isValid(99); // true
$valid->isValid(42); // false
```

## Validators

### `AlwaysValid`
Expand Down Expand Up @@ -113,15 +130,33 @@ $valid = new \Alley\Validator\Comparison(
$valid->isValid(true); // true
```

### `Not`

`Alley\Validator\Not` inverts the validity of a given validator. It allows for creating validators that test whether input is, for example, "not one of" in addition to "one of."

#### Supported options

None.

#### Basic usage

```php
<?php

$origin = new \Alley\Validator\OneOf(['haystack' => ['foo', 'bar']]);
$valid = new Alley\Validator\Not($origin, 'The input was invalid.');

$valid->isValid('foo'); // false
$valid->isValid('baz'); // true
```

### `OneOf`

`Alley\Validator\OneOf` validates whether an array of scalar values contains the input.

`OneOf` is a simpler version of `\Laminas\Validator\InArray` that accepts only scalar values in the haystack and does only strict comparisons. In return, it produces a friendlier error message that lists the allowed values.

`OneOf` contains a `::create()` named constructor for initializing an instance directly from the haystack.

#### Supported Options
#### Supported options

The following options are supported for `\Alley\Validator\OneOf`:

Expand Down
91 changes: 91 additions & 0 deletions src/Alley/Validator/AnyValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

/*
* This file is part of the laminas-validator-extensions package.
*
* (c) Alley <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Alley\Validator;

use Countable;
use Laminas\Validator\ValidatorInterface;
use ReturnTypeWillChange;

final class AnyValidator implements Countable, ValidatorInterface
{
/**
* Validator chain.
*
* @var ValidatorInterface[]
*/
protected array $validators = [];

/**
* Array of validation failure messages.
*
* @var string[]
*/
protected $messages = [];

/**
* @param ValidatorInterface[] $validators
*/
public function __construct(array $validators)
{
foreach ($validators as $validator) {
$this->attach($validator);
}
}

/**
* Attach a validator to the end of the chain.
*
* @param ValidatorInterface $validator
* @return self
*/
public function attach(ValidatorInterface $validator)
{
$this->validators[] = $validator;

return $this;
}

public function isValid($value)
{
$this->messages = [];

if ($this->count() === 0) {
// Consistent with `\Laminas\Validator\ValidatorChain()`.
return true;
}

foreach ($this->validators as $validator) {
if ($validator->isValid($value)) {
return true;
}
}

foreach ($this->validators as $validator) {
$this->messages = array_replace($this->messages, $validator->getMessages());
}

return false;
}

public function getMessages()
{
return $this->messages;
}

#[ReturnTypeWillChange]
public function count()
{
return \count($this->validators);
}
}
2 changes: 1 addition & 1 deletion src/Alley/Validator/BaseValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ abstract class BaseValidator extends AbstractValidator
final public function isValid($value): bool
{
$this->setValue($value);
$this->testValue($value);
$this->testValue($this->value);
return \count($this->getMessages()) === 0;
}

Expand Down
45 changes: 45 additions & 0 deletions src/Alley/Validator/Not.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* This file is part of the laminas-validator-extensions package.
*
* (c) Alley <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Alley\Validator;

use Laminas\Validator\ValidatorInterface;

final class Not implements ValidatorInterface
{
private ValidatorInterface $origin;

private string $message;

public function __construct(ValidatorInterface $origin, string $message)
{
$this->origin = $origin;
$this->message = $message;
}

public function isValid($value)
{
return !$this->origin->isValid($value);
}

public function getMessages()
{
$messages = [];

if (\count($this->origin->getMessages()) === 0) {
$messages[] = $this->message;
}

return $messages;
}
}
47 changes: 47 additions & 0 deletions tests/Alley/Validator/AnyValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/*
* This file is part of the laminas-validator-extensions package.
*
* (c) Alley <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Alley\Validator;

use Laminas\Validator\GreaterThan;
use PHPUnit\Framework\TestCase;

final class AnyValidatorTest extends TestCase
{
public function testNoValidators()
{
$validator = new AnyValidator([]);
$this->assertTrue($validator->isValid(42));
}

public function testValidValidator()
{
$validator = new AnyValidator([new AlwaysValid()]);
$this->assertTrue($validator->isValid(42));
}

public function testInvalidValidator()
{
$validator = new AnyValidator([new GreaterThan(['min' => 43])]);
$this->assertFalse($validator->isValid(42));
}

public function testFirstValidValidator()
{
$validator = new AnyValidator([
new AlwaysValid(),
new GreaterThan(['min' => 43]),
]);
$this->assertTrue($validator->isValid(42));
}
}
28 changes: 28 additions & 0 deletions tests/Alley/Validator/NotTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the laminas-validator-extensions package.
*
* (c) Alley <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Alley\Validator;

use PHPUnit\Framework\TestCase;

final class NotTest extends TestCase
{
public function testNegation()
{
$origin = new AlwaysValid();
$actual = new Not($origin, 'foo');
$value = 42;
$this->assertNotSame($actual->isValid($value), $origin->isValid($value));
$this->assertCount(1, $actual->getMessages());
}
}

0 comments on commit e60c333

Please sign in to comment.