diff --git a/packages/spa/defaults/translations/en/validation.php b/packages/spa/defaults/translations/en/validation.php index 9d02d0779..8a7c82d78 100644 --- a/packages/spa/defaults/translations/en/validation.php +++ b/packages/spa/defaults/translations/en/validation.php @@ -1,6 +1,7 @@ 'The :attribute is invalid.', 'bool' => 'The :attribute is not a boolean.', 'string' => 'The :attribute is not a string.', 'int' => 'The :attribute is not an integer.', diff --git a/packages/spa/src/Validation/Rules/BoolRule.php b/packages/spa/src/Validation/Rules/BoolRule.php index e5a7720a2..519ec028a 100644 --- a/packages/spa/src/Validation/Rules/BoolRule.php +++ b/packages/spa/src/Validation/Rules/BoolRule.php @@ -10,11 +10,8 @@ * Boolean value. */ class BoolRule extends Rule { - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { + public function isValid(string $attribute, mixed $value): bool { return is_bool($value); } } diff --git a/packages/spa/src/Validation/Rules/BoolRuleTest.php b/packages/spa/src/Validation/Rules/BoolRuleTest.php index 23cb4f368..29aacf243 100644 --- a/packages/spa/src/Validation/Rules/BoolRuleTest.php +++ b/packages/spa/src/Validation/Rules/BoolRuleTest.php @@ -3,7 +3,7 @@ namespace LastDragon_ru\LaraASP\Spa\Validation\Rules; use Illuminate\Container\Container; -use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -15,19 +15,32 @@ final class BoolRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, mixed $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new BoolRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(BoolRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); - self::assertEquals($expected, $rule->passes('attribute', $value)); + self::assertEquals($expected, !$validator->fails()); + + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not a boolean.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new BoolRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(BoolRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not a boolean.', $rule->message()); + self::assertEquals($expected, $actual); } // @@ -36,7 +49,7 @@ public function testMessage(): void { /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'true' => [true, true], 'false' => [true, false], diff --git a/packages/spa/src/Validation/Rules/DateRule.php b/packages/spa/src/Validation/Rules/DateRule.php index ed8e2fdf2..1837afc91 100644 --- a/packages/spa/src/Validation/Rules/DateRule.php +++ b/packages/spa/src/Validation/Rules/DateRule.php @@ -13,21 +13,18 @@ * ISO 8601 Date. */ class DateRule extends Rule implements ValueProvider { - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { - $passes = false; + public function isValid(string $attribute, mixed $value): bool { + $valid = false; try { - $date = $this->getValue($value); - $passes = $date && $date->format($this->getFormat()) === $value; + $date = $this->getValue($value); + $valid = $date && $date->format($this->getFormat()) === $value; } catch (InvalidArgumentException $exception) { // ignored } - return $passes; + return $valid; } #[Override] diff --git a/packages/spa/src/Validation/Rules/DateRuleTest.php b/packages/spa/src/Validation/Rules/DateRuleTest.php index c52363907..a71eaac0f 100644 --- a/packages/spa/src/Validation/Rules/DateRuleTest.php +++ b/packages/spa/src/Validation/Rules/DateRuleTest.php @@ -5,6 +5,7 @@ use Exception; use Illuminate\Container\Container; use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use InvalidArgumentException; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; @@ -17,19 +18,32 @@ final class DateRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, string $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new DateRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(DateRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); + + self::assertEquals($expected, !$validator->fails()); - self::assertEquals($expected, $rule->passes('attribute', $value)); + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not a valid date.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new DateRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, string $value): void { + $rule = Container::getInstance()->make(DateRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not a valid date.', $rule->message()); + self::assertEquals($expected, $actual); } #[DataProvider('dataProviderGetValue')] @@ -51,7 +65,7 @@ public function testGetValue(Exception|string|null $expected, string $value): vo /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'valid date' => [true, '2102-12-01'], 'invalid date' => [false, '02-12-01'], diff --git a/packages/spa/src/Validation/Rules/DateTimeRuleTest.php b/packages/spa/src/Validation/Rules/DateTimeRuleTest.php index 299e324f1..0960dffae 100644 --- a/packages/spa/src/Validation/Rules/DateTimeRuleTest.php +++ b/packages/spa/src/Validation/Rules/DateTimeRuleTest.php @@ -4,6 +4,7 @@ use Illuminate\Container\Container; use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use InvalidArgumentException; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; @@ -19,19 +20,32 @@ final class DateTimeRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, string $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new DateTimeRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(DateTimeRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); - self::assertEquals($expected, $rule->passes('attribute', $value)); + self::assertEquals($expected, !$validator->fails()); + + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not a valid datetime.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new DateTimeRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, string $value): void { + $rule = Container::getInstance()->make(DateTimeRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not a valid datetime.', $rule->message()); + self::assertEquals($expected, $actual); } /** @@ -62,7 +76,7 @@ public function testGetValue(string|array $expected, ?string $tz, string $value) /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'valid date' => [false, '2102-12-01'], 'invalid date' => [false, '02-12-01'], diff --git a/packages/spa/src/Validation/Rules/IntRule.php b/packages/spa/src/Validation/Rules/IntRule.php index cd42068d6..bbf7d0b2d 100644 --- a/packages/spa/src/Validation/Rules/IntRule.php +++ b/packages/spa/src/Validation/Rules/IntRule.php @@ -10,11 +10,8 @@ * Int value. */ class IntRule extends Rule { - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { + public function isValid(string $attribute, mixed $value): bool { return is_int($value); } } diff --git a/packages/spa/src/Validation/Rules/IntRuleTest.php b/packages/spa/src/Validation/Rules/IntRuleTest.php index d80f2d630..cc1377c83 100644 --- a/packages/spa/src/Validation/Rules/IntRuleTest.php +++ b/packages/spa/src/Validation/Rules/IntRuleTest.php @@ -3,7 +3,7 @@ namespace LastDragon_ru\LaraASP\Spa\Validation\Rules; use Illuminate\Container\Container; -use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -18,19 +18,32 @@ final class IntRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, mixed $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new IntRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(IntRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); - self::assertEquals($expected, $rule->passes('attribute', $value)); + self::assertEquals($expected, !$validator->fails()); + + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not an integer.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new IntRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(IntRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not an integer.', $rule->message()); + self::assertEquals($expected, $actual); } // @@ -39,7 +52,7 @@ public function testMessage(): void { /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'true' => [false, true], 'false' => [false, false], diff --git a/packages/spa/src/Validation/Rules/NumberRule.php b/packages/spa/src/Validation/Rules/NumberRule.php index 06ba544b4..936185861 100644 --- a/packages/spa/src/Validation/Rules/NumberRule.php +++ b/packages/spa/src/Validation/Rules/NumberRule.php @@ -11,11 +11,8 @@ * Number value. */ class NumberRule extends IntRule { - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { - return (is_float($value) && is_finite($value)) || parent::passes($attribute, $value); + public function isValid(string $attribute, mixed $value): bool { + return (is_float($value) && is_finite($value)) || parent::isValid($attribute, $value); } } diff --git a/packages/spa/src/Validation/Rules/NumberRuleTest.php b/packages/spa/src/Validation/Rules/NumberRuleTest.php index 38e4c7cd0..8eb7bd9cb 100644 --- a/packages/spa/src/Validation/Rules/NumberRuleTest.php +++ b/packages/spa/src/Validation/Rules/NumberRuleTest.php @@ -3,7 +3,7 @@ namespace LastDragon_ru\LaraASP\Spa\Validation\Rules; use Illuminate\Container\Container; -use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -18,19 +18,32 @@ final class NumberRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, mixed $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new NumberRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(NumberRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); - self::assertEquals($expected, $rule->passes('attribute', $value)); + self::assertEquals($expected, !$validator->fails()); + + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not an number.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new NumberRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(NumberRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not an number.', $rule->message()); + self::assertEquals($expected, $actual); } // @@ -39,7 +52,7 @@ public function testMessage(): void { /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'true' => [false, true], 'false' => [false, false], diff --git a/packages/spa/src/Validation/Rules/ResolverRule.php b/packages/spa/src/Validation/Rules/ResolverRule.php index 7c0073fc4..e5a30c708 100644 --- a/packages/spa/src/Validation/Rules/ResolverRule.php +++ b/packages/spa/src/Validation/Rules/ResolverRule.php @@ -9,7 +9,6 @@ use Override; use function array_merge; -use function get_class; class ResolverRule extends Rule implements ValueProvider { protected Resolver $resolver; @@ -22,11 +21,8 @@ public function __construct(Translator $translator, Resolver $resolver) { // // ========================================================================= - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { + public function isValid(string $attribute, mixed $value): bool { try { return (bool) $this->getValue($value); } catch (UnresolvedValueException $exception) { @@ -42,7 +38,7 @@ public function passes($attribute, $value) { #[Override] protected function getMessageReplace(): array { return array_merge(parent::getMessageReplace(), [ - 'resolver' => get_class($this->resolver), + 'resolver' => $this->resolver::class, ]); } @@ -52,7 +48,7 @@ protected function getMessageReplace(): array { #[Override] protected function getMessageVariants(): array { $defaults = parent::getMessageVariants(); - $resolver = get_class($this->resolver); + $resolver = $this->resolver::class; $variants = []; $customized = []; diff --git a/packages/spa/src/Validation/Rules/ResolverRuleTest.php b/packages/spa/src/Validation/Rules/ResolverRuleTest.php index 474be7d23..b7666a6c2 100644 --- a/packages/spa/src/Validation/Rules/ResolverRuleTest.php +++ b/packages/spa/src/Validation/Rules/ResolverRuleTest.php @@ -4,6 +4,7 @@ use Illuminate\Container\Container; use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use Illuminate\Routing\Router; use LastDragon_ru\LaraASP\Spa\Routing\Resolver; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; @@ -18,7 +19,7 @@ final class ResolverRuleTest extends TestCase { // // ========================================================================= - public function testPasses(): void { + public function testRule(): void { $translator = Container::getInstance()->make(Translator::class); $router = Container::getInstance()->make(Router::class); $resolver = new class($router) extends Resolver { @@ -27,15 +28,34 @@ public function testPasses(): void { */ #[Override] protected function resolve(mixed $value, array $parameters): mixed { - return new stdClass(); + return $value ? new stdClass() : null; } }; $rule = new ResolverRule($translator, $resolver); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make( + [ + 'a' => true, + 'b' => false, + ], + [ + 'a' => $rule, + 'b' => $rule, + ], + ); - self::assertTrue($rule->passes('attribute', 'value')); + self::assertTrue($validator->fails()); + self::assertEquals( + [ + 'b' => [ + 'The b not found.', + ], + ], + $validator->errors()->toArray(), + ); } - public function testPassesUnresolved(): void { + public function testIsValid(): void { $translator = Container::getInstance()->make(Translator::class); $router = Container::getInstance()->make(Router::class); $resolver = new class($router) extends Resolver { @@ -44,15 +64,15 @@ public function testPassesUnresolved(): void { */ #[Override] protected function resolve(mixed $value, array $parameters): mixed { - return null; + return new stdClass(); } }; $rule = new ResolverRule($translator, $resolver); - self::assertFalse($rule->passes('attribute', 'value')); + self::assertTrue($rule->isValid('attribute', 'value')); } - public function testMessage(): void { + public function testIsValidUnresolved(): void { $translator = Container::getInstance()->make(Translator::class); $router = Container::getInstance()->make(Router::class); $resolver = new class($router) extends Resolver { @@ -61,12 +81,12 @@ public function testMessage(): void { */ #[Override] protected function resolve(mixed $value, array $parameters): mixed { - return new stdClass(); + return null; } }; $rule = new ResolverRule($translator, $resolver); - self::assertEquals('The :attribute not found.', $rule->message()); + self::assertFalse($rule->isValid('attribute', 'value')); } // } diff --git a/packages/spa/src/Validation/Rules/Rule.php b/packages/spa/src/Validation/Rules/Rule.php index e52624f07..97657cfc9 100644 --- a/packages/spa/src/Validation/Rules/Rule.php +++ b/packages/spa/src/Validation/Rules/Rule.php @@ -2,46 +2,108 @@ namespace LastDragon_ru\LaraASP\Spa\Validation\Rules; +use Closure; use Illuminate\Contracts\Translation\Translator; use Illuminate\Contracts\Validation\Rule as RuleContract; +use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Collection; use Illuminate\Support\Str; use LastDragon_ru\LaraASP\Spa\Package; use Override; use ReflectionClass; +use function assert; +use function is_string; use function preg_replace; +use function trigger_deprecation; -abstract class Rule implements RuleContract { +abstract class Rule implements RuleContract, ValidationRule { protected Translator $translator; public function __construct(Translator $translator) { $this->translator = $translator; } + // + // ========================================================================= + #[Override] + public function validate(string $attribute, mixed $value, Closure $fail): void { + if (!$this->isValid($attribute, $value)) { + $fail($this->getMessage()); + } + } + + protected function isValid(string $attribute, mixed $value): bool { + trigger_deprecation( + Package::Name, + '%{VERSION}', + 'Implementing `%s` is deprecated, use `%s` instead.', + RuleContract::class, + ValidationRule::class, + ); + + return $this->passes($attribute, $value); + } + // + // // ========================================================================= /** + * @inheritDoc + */ + #[Override] + public function passes($attribute, $value): bool { + trigger_deprecation( + Package::Name, + '%{VERSION}', + 'Implementing `%s` is deprecated, use `%s` instead.', + RuleContract::class, + ValidationRule::class, + ); + + return false; + } + + /** + * @deprecated %{VERSION} + * * @return array|string */ #[Override] public function message(): array|string { + trigger_deprecation( + Package::Name, + '%{VERSION}', + 'Implementing `%s` is deprecated, use `%s` instead.', + RuleContract::class, + ValidationRule::class, + ); + + return $this->getMessage(); + } + // + + // + // ========================================================================= + protected function getMessage(): string { $replace = $this->getMessageReplace(); $variants = $this->getMessageVariants(); $translation = (new Collection($variants)) ->mapWithKeys(function (string $variant) use ($replace) { - return [$variant => $this->translator->get($variant, $replace)]; + return [$variant => $this->translate($variant, $replace)]; }) ->first(static function (string $value, string $key): bool { return $key !== $value; - }); + }) + ?: $this->getMessageDefault(); - return $translation ?? []; + return $translation; + } + + protected function getMessageDefault(): string { + return $this->translate(Package::Name.'::validation.default'); } - // - // - // ========================================================================= /** * @return array */ @@ -62,5 +124,16 @@ protected function getMessageVariants(): array { "{$package}::validation.{$name}", // package ]; } + + /** + * @param array $replace + */ + private function translate(string $string, array $replace = []): string { + $translated = $this->translator->get($string, $replace); + + assert(is_string($translated)); + + return $translated; + } // } diff --git a/packages/spa/src/Validation/Rules/StringRule.php b/packages/spa/src/Validation/Rules/StringRule.php index 34385c158..9b435f066 100644 --- a/packages/spa/src/Validation/Rules/StringRule.php +++ b/packages/spa/src/Validation/Rules/StringRule.php @@ -10,11 +10,8 @@ * String value. */ class StringRule extends Rule { - /** - * @inheritDoc - */ #[Override] - public function passes($attribute, $value) { + public function isValid(string $attribute, mixed $value): bool { return is_string($value); } } diff --git a/packages/spa/src/Validation/Rules/StringRuleTest.php b/packages/spa/src/Validation/Rules/StringRuleTest.php index 3f2f0a664..f784e8494 100644 --- a/packages/spa/src/Validation/Rules/StringRuleTest.php +++ b/packages/spa/src/Validation/Rules/StringRuleTest.php @@ -3,7 +3,7 @@ namespace LastDragon_ru\LaraASP\Spa\Validation\Rules; use Illuminate\Container\Container; -use Illuminate\Contracts\Translation\Translator; +use Illuminate\Contracts\Validation\Factory; use LastDragon_ru\LaraASP\Spa\Testing\Package\TestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -15,19 +15,32 @@ final class StringRuleTest extends TestCase { // // ========================================================================= - #[DataProvider('dataProviderPasses')] - public function testPasses(bool $expected, mixed $value): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new StringRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testRule(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(StringRule::class); + $factory = Container::getInstance()->make(Factory::class); + $validator = $factory->make(['value' => $value], ['value' => $rule]); - self::assertEquals($expected, $rule->passes('attribute', $value)); + self::assertEquals($expected, !$validator->fails()); + + if ($expected === false) { + self::assertEquals( + [ + 'value' => [ + 'The value is not a string.', + ], + ], + $validator->errors()->toArray(), + ); + } } - public function testMessage(): void { - $translator = Container::getInstance()->make(Translator::class); - $rule = new StringRule($translator); + #[DataProvider('dataProviderIsValid')] + public function testIsValid(bool $expected, mixed $value): void { + $rule = Container::getInstance()->make(StringRule::class); + $actual = $rule->isValid('attribute', $value); - self::assertEquals('The :attribute is not a string.', $rule->message()); + self::assertEquals($expected, $actual); } // @@ -36,7 +49,7 @@ public function testMessage(): void { /** * @return array */ - public static function dataProviderPasses(): array { + public static function dataProviderIsValid(): array { return [ 'true' => [false, true], 'false' => [false, false],