From 66699d588eb421c707ad4ac425537306133c2278 Mon Sep 17 00:00:00 2001 From: macocci7 Date: Sun, 28 Apr 2024 22:41:46 +0900 Subject: [PATCH] Add Instance Rule Object --- README.md | 53 ++++++++++++++++++ composer.json | 2 +- examples/ValidateInstance.php | 31 +++++++++++ src/Rules/Instance.php | 59 ++++++++++++++++++++ src/lang/en/validation.php | 1 + src/lang/ja/validation.php | 1 + tests/Rules/InstanceTest.php | 84 +++++++++++++++++++++++++++++ tests/Rules/PasswordWrapperTest.php | 5 ++ 8 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 examples/ValidateInstance.php create mode 100644 src/Rules/Instance.php create mode 100644 tests/Rules/InstanceTest.php diff --git a/README.md b/README.md index 0852d71..20b831c 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,27 @@ $validator = Validator::make( ); ``` +Additionally and uniquely, `Instance` rule object is supported. + +```php +$validator = Validator::make( + data: [ + 'prop1' => new Instance([]), + 'prop2' => 'Instance', + 'prop3' => fn () => true, + ], + rules: [ + 'prop1' => Instance::of(Instance::class), + 'prop2' => Instance::of([ + Instance::class, + Validator::class, + (fn () => true)::class, + ]), + 'prop3' => Instance::of('Closure'), + ], +); +``` + ## 2. Contents - [1. Features](#1-features) @@ -46,6 +67,7 @@ $validator = Validator::make( - [5.2. Setting Traslations Root Path and Language](#52-setting-translations-root-path-and-language) - [5.3. Using Passowrd Rule Object](#53-using-password-rule-object) - [5.4. Using File Rule Object](#54-using-file-rule-object) + - [5.5. Using Instance Rule Object](#55-using-instance-rule-object) - [6. Examples](#6-examples) - [7. LICENSE](#7-license) @@ -193,12 +215,43 @@ You can learn more about Laravel's `File` rule object at the [Laravel Official D Here's an example code for using Laravel's `File` rule object: [ValidateFile.php](examples/ValidateFile.php) +### 5.5. Using Instance Rule Object + +You can validate objects using `Instance` rule object. (unique feature) + +By using `Instance::of($class)` method as a rule, you can perform `$value instanceof $class` in the validation. + +`Instance::of()` accepts class name(s) as an argument. + +```php +use Macocci7\PurephpValidation\Rules\Instance; + +$validator = Validator::make( + data: $data, + rules: [ + 'prop1' => Instance::of(Instance::class), + 'prop2' => Instance::of([ + // Macocci7\PurephpValidation\Rules\Instance + Instance::class, + // Macocci7\PurephpValidation\ValidatorFactory + Validator::class, + // Closure + (fn () => true)::class, + ]), + 'prop3' => Instance::of('Closure'), + ], +); +``` + +Here's an example code for using `Instance` rule object: [ValidateInstance.php](examples/ValidateInstance.php) + ## 6. Examples - [BasicUsage.php](examples/BasicUsage.php) - [SetTranslationsRootPath.php](examples/SetTranslationsRootPath.php) - [ValidatePassword.php](examples/ValidatePassword.php) - [ValidateFile.php](examples/ValidateFile.php) +- [ValidateInstance.php](examples/ValidateInstance.php) ## 7. LICENSE diff --git a/composer.json b/composer.json index 94206a0..70c1ac1 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "macocci7/purephp-validation", "description": "illuminate/validation wrapper for pure php.", - "version": "0.0.2", + "version": "0.0.3", "type": "library", "license": "MIT", "autoload": { diff --git a/examples/ValidateInstance.php b/examples/ValidateInstance.php new file mode 100644 index 0000000..23b3e56 --- /dev/null +++ b/examples/ValidateInstance.php @@ -0,0 +1,31 @@ + new Instance([]), + 'prop2' => 'Instance', + 'prop3' => fn () => true, + ], + rules: [ + 'prop1' => Instance::of(Instance::class), + 'prop2' => Instance::of([ + Instance::class, + Validator::class, + (fn () => true)::class, + ]), + 'prop3' => Instance::of('Closure'), + ], +); + +// Checking result +if ($validator->fails()) { + var_dump($validator->errors()->messages()); +} else { + echo "🎊 passed 🎉" . PHP_EOL; +} diff --git a/src/Rules/Instance.php b/src/Rules/Instance.php new file mode 100644 index 0000000..2d85cfe --- /dev/null +++ b/src/Rules/Instance.php @@ -0,0 +1,59 @@ +classes = $classes; + } + + /** + * Run the validation rule. + * + * @param string $attribute + * @param mixed $value + * @param Closure $fail + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + $matched = false; + foreach ($this->classes as $class) { + if ($value instanceof $class) { + $matched = true; + break; + } + } + if (!$matched) { + $fail('validation.instance')->translate([ + 'classes' => implode(', ', $this->classes), + ]); + } + } + + /** + * Sets class names and returns an instance of this class + * + * @param string|string[] $class + * @return Instance + */ + public static function of(string|array $class) + { + return new static(is_string($class) ? [$class] : $class); + } +} diff --git a/src/lang/en/validation.php b/src/lang/en/validation.php index c3e94e1..558e448 100644 --- a/src/lang/en/validation.php +++ b/src/lang/en/validation.php @@ -71,6 +71,7 @@ 'image' => 'The :attribute field must be an image.', 'in' => 'The selected :attribute is invalid.', 'in_array' => 'The :attribute field must exist in :other.', + 'instance' => 'The :attribute must be an instance of: :classes.', 'integer' => 'The :attribute field must be an integer.', 'ip' => 'The :attribute field must be a valid IP address.', 'ipv4' => 'The :attribute field must be a valid IPv4 address.', diff --git a/src/lang/ja/validation.php b/src/lang/ja/validation.php index aeb945b..6f03655 100644 --- a/src/lang/ja/validation.php +++ b/src/lang/ja/validation.php @@ -62,6 +62,7 @@ 'image' => ':attributeには画像ファイルを指定してください。', 'in' => '選択された:attributeは正しくありません。', 'in_array' => ':attributeには:otherの値を指定してください。', + 'instance' => ':attributeは次のいずれかのインスタンスを指定してください: :classes.', 'integer' => ':attributeは整数で指定してください。', 'ip' => ':attributeには、有効なIPアドレスを指定してください。', 'ipv4' => ':attributeには、有効なIPv4アドレスを指定してください。', diff --git a/tests/Rules/InstanceTest.php b/tests/Rules/InstanceTest.php new file mode 100644 index 0000000..d7b435a --- /dev/null +++ b/tests/Rules/InstanceTest.php @@ -0,0 +1,84 @@ + [ + 'lang' => 'en', + 'data' => [ + 'prop1' => new Instance([]), + 'prop2' => 'Instance', + 'prop3' => fn () => true, + ], + 'rules' => [ + 'prop1' => Instance::of(Instance::class), + 'prop2' => Instance::of([ + Instance::class, + ValidatorFactory::class, + (fn () => true)::class, + ]), + 'prop3' => Instance::of('Closure'), + ], + ], + 'expected' => [ + 'fails' => true, + 'messages' => [ + 'prop2' => ['The prop2 must be an instance of: Macocci7\PurephpValidation\Rules\Instance, Macocci7\PurephpValidation\ValidatorFactory, Closure.'], + ], + ], + ], + [ + 'input' => [ + 'lang' => 'ja', + 'data' => [ + 'prop1' => new Instance([]), + 'prop2' => 'Instance', + 'prop3' => fn () => true, + ], + 'rules' => [ + 'prop1' => Instance::of(Instance::class), + 'prop2' => Instance::of([ + Instance::class, + ValidatorFactory::class, + (fn () => true)::class, + ]), + 'prop3' => Instance::of('Closure'), + ], + ], + 'expected' => [ + 'fails' => true, + 'messages' => [ + 'prop2' => ['prop2は次のいずれかのインスタンスを指定してください: Macocci7\PurephpValidation\Rules\Instance, Macocci7\PurephpValidation\ValidatorFactory, Closure.'], + ], + ], + ], + ]; + } + + #[DataProvider('provide_of_can_work_correctly')] + public function test_of_can_work_correctly(array $input, array $expected): void + { + ValidatorFactory::lang($input['lang']); + $validator = ValidatorFactory::make($input['data'], $input['rules']); + $this->assertSame($expected['fails'], $validator->fails()); + if ($validator->fails()) { + $this->assertSame($expected['messages'], $validator->errors()->messages()); + } + } +} diff --git a/tests/Rules/PasswordWrapperTest.php b/tests/Rules/PasswordWrapperTest.php index 8babc91..b04b67c 100644 --- a/tests/Rules/PasswordWrapperTest.php +++ b/tests/Rules/PasswordWrapperTest.php @@ -19,6 +19,7 @@ public static function provide_password_rule_can_work_correctly(): array return [ [ 'input' => [ + 'lang' => 'en', 'data' => [ 'password' => 'passwor', ], @@ -35,6 +36,7 @@ public static function provide_password_rule_can_work_correctly(): array ], [ 'input' => [ + 'lang' => 'en', 'data' => [ 'password' => 'password', ], @@ -50,6 +52,7 @@ public static function provide_password_rule_can_work_correctly(): array ], [ 'input' => [ + 'lang' => 'en', 'data' => [ 'password' => 'passwordpasswordpassword', ], @@ -66,6 +69,7 @@ public static function provide_password_rule_can_work_correctly(): array ], [ 'input' => [ + 'lang' => 'en', 'data' => [ 'password' => 'password', ], @@ -98,6 +102,7 @@ public static function provide_password_rule_can_work_correctly(): array #[DataProvider('provide_password_rule_can_work_correctly')] public function test_password_rule_can_work_correctly(array $input, array $expected): void { + ValidatorFactory::lang($input['lang']); $validator = ValidatorFactory::make($input['data'], $input['rules']); $this->assertSame($expected['fails'], $validator->fails()); if ($validator->fails()) {