Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonTheAdams committed Feb 7, 2023
2 parents 6ce71f3 + 756a105 commit cc7c12a
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 36 deletions.
14 changes: 14 additions & 0 deletions src/Commands/SkipValidationRules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace StellarWP\Validation\Commands;

/**
* Returning this command from ValidationRule::__invoke() tells the Validator to skip all subsequent rules.
*
* @unreleased
*/
class SkipValidationRules
{
}
3 changes: 2 additions & 1 deletion src/Contracts/ValidationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace StellarWP\Validation\Contracts;

use Closure;
use StellarWP\Validation\Commands\SkipValidationRules;

interface ValidationRule
{
Expand All @@ -31,7 +32,7 @@ public static function fromString(string $options = null): ValidationRule;
*
* @since 1.0.0
*
* @return void
* @return void|SkipValidationRules
*/
public function __invoke($value, Closure $fail, string $key, array $values);
}
55 changes: 55 additions & 0 deletions src/Rules/Nullable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace StellarWP\Validation\Rules;

use Closure;
use StellarWP\Validation\Commands\SkipValidationRules;
use StellarWP\Validation\Contracts\ValidatesOnFrontEnd;
use StellarWP\Validation\Contracts\ValidationRule;

/**
* This rule skips further validation if the field is null. It is similar to Optional, but the only allowed value is
* null.
*
* @unreleased
*/
class Nullable implements ValidationRule, ValidatesOnFrontEnd
{
/**
* @unreleased
*/
public static function id(): string
{
return 'nullable';
}

/**
* @unreleased
*/
public static function fromString(string $options = null): ValidationRule
{
return new self();
}

/**
* @unreleased
*
* @return SkipValidationRules|void
*/
public function __invoke($value, Closure $fail, string $key, array $values)
{
if ($value === null) {
return new SkipValidationRules();
}
}

/**
* @unreleased
*/
public function serializeOption()
{
return null;
}
}
54 changes: 54 additions & 0 deletions src/Rules/Optional.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

namespace StellarWP\Validation\Rules;

use Closure;
use StellarWP\Validation\Commands\SkipValidationRules;
use StellarWP\Validation\Contracts\ValidatesOnFrontEnd;
use StellarWP\Validation\Contracts\ValidationRule;

/**
* This rule marks a field as optional and skips further validation if the rule is either null or an empty string.
*
* @unreleased
*/
class Optional implements ValidationRule, ValidatesOnFrontEnd
{
/**
* @unreleased
*/
public static function id(): string
{
return 'optional';
}

/**
* @unreleased
*/
public static function fromString(string $options = null): ValidationRule
{
return new self();
}

/**
* @unreleased
*
* @return SkipValidationRules|void
*/
public function __invoke($value, Closure $fail, string $key, array $values)
{
if ($value === null || $value === '') {
return new SkipValidationRules();
}
}

/**
* @unreleased
*/
public function serializeOption()
{
return null;
}
}
4 changes: 4 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use StellarWP\Validation\Rules\Integer;
use StellarWP\Validation\Rules\Max;
use StellarWP\Validation\Rules\Min;
use StellarWP\Validation\Rules\Nullable;
use StellarWP\Validation\Rules\Numeric;
use StellarWP\Validation\Rules\Optional;
use StellarWP\Validation\Rules\Required;
use StellarWP\Validation\Rules\Size;

Expand All @@ -24,6 +26,8 @@ class ServiceProvider
Integer::class,
Email::class,
Currency::class,
Nullable::class,
Optional::class,
];

/**
Expand Down
27 changes: 6 additions & 21 deletions src/Validator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace StellarWP\Validation;

use StellarWP\Validation\Commands\SkipValidationRules;
use StellarWP\Validation\Contracts\Sanitizer;

/**
Expand Down Expand Up @@ -51,8 +52,6 @@ class Validator
*/
public function __construct(array $ruleSets, array $values, array $labels = [])
{
$this->validateRulesAndValues($ruleSets, $values);

$validatedRules = [];
foreach ($ruleSets as $key => $rule) {
if (is_array($rule)) {
Expand All @@ -71,24 +70,6 @@ public function __construct(array $ruleSets, array $values, array $labels = [])
$this->labels = $labels;
}

/**
* Validates that all rules have a corresponding value with the same key.
*
* @since 1.0.0
*
* @return void
*/
private function validateRulesAndValues(array $rules, array $values)
{
$missingKeys = array_diff_key($rules, $values);

if (!empty($missingKeys)) {
Config::throwInvalidArgumentException(
"Missing values for rules: " . implode(', ', array_keys($missingKeys))
);
}
}

/**
* Returns whether the values failed validation or not.
*
Expand Down Expand Up @@ -134,7 +115,11 @@ private function runValidationRules()
};

foreach ($ruleSet as $rule) {
$rule($value, $fail, $key, $this->values);
$command = $rule($value, $fail, $key, $this->values);

if ($command instanceof SkipValidationRules) {
break;
}

if ($rule instanceof Sanitizer) {
$value = $rule->sanitize($value);
Expand Down
30 changes: 30 additions & 0 deletions tests/_support/Helper/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,36 @@ public static function assertValidationRuleFailed(
self::assertValidationRulePassed($rule, $value, $key, $values, false);
}

public static function assertValidationRuleDoesReturnCommandInstance(
ValidationRule $rule,
string $commandClass,
$value = null,
string $key = '',
array $values = []
) {
$fail = static function () {
};

$command = $rule($value, $fail, $key, $values);

self::assertInstanceOf($commandClass, $command);
}

public static function assertValidationRuleDoesNotReturnCommandInstance(
ValidationRule $rule,
string $commandClass,
$value = null,
string $key = '',
array $values = []
) {
$fail = static function () {
};

$command = $rule($value, $fail, $key, $values);

self::assertNotInstanceOf($commandClass, $command);
}

public static function assertIsIterable($actual, $message = '')
{
if (\function_exists('is_iterable') === true) {
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/Rules/NullableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace StellarWP\Validation\Tests\Unit\Rules;

use StellarWP\Validation\Commands\SkipValidationRules;
use StellarWP\Validation\Rules\Nullable;
use StellarWP\Validation\Tests\TestCase;

class NullableTest extends TestCase
{
/**
* @unreleased
*/
public function testNullableValidation()
{
$rule = new Nullable();

// Passes when value is null and skips remaining tests
self::assertValidationRulePassed($rule, null);
self::assertValidationRuleDoesReturnCommandInstance($rule, SkipValidationRules::class, null);

// Passes on any other value but does not skip remaining tests
self::assertValidationRulePassed($rule, 'bar');
self::assertValidationRuleDoesNotReturnCommandInstance($rule, SkipValidationRules::class, 'bar');
}
}
32 changes: 32 additions & 0 deletions tests/unit/Rules/OptionalTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace StellarWP\Validation\Tests\Unit\Rules;

use StellarWP\Validation\Commands\SkipValidationRules;
use StellarWP\Validation\Rules\Optional;
use StellarWP\Validation\Tests\TestCase;

class OptionalTest extends TestCase
{
/**
* @unreleased
*/
public function testNullableValidation()
{
$rule = new Optional();

// Passes when value is null and skips remaining tests
self::assertValidationRulePassed($rule, null);
self::assertValidationRuleDoesReturnCommandInstance($rule, SkipValidationRules::class, null);

// Passes when value is empty string and skips remaining tests
self::assertValidationRulePassed($rule, '');
self::assertValidationRuleDoesReturnCommandInstance($rule, SkipValidationRules::class, '');

// Passes on any other value but does not skip remaining tests
self::assertValidationRulePassed($rule, 'bar');
self::assertValidationRuleDoesNotReturnCommandInstance($rule, SkipValidationRules::class, 'bar');
}
}
Loading

0 comments on commit cc7c12a

Please sign in to comment.