From 459d4b63b0d5d96d588c706557c0ea68dc129b06 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Sun, 1 Nov 2020 11:31:16 -0500 Subject: [PATCH] Validator extender (#2102) Added validator extender, integration tests, and deprecated related Validating event --- framework/core/src/Extend/Validator.php | 58 +++++++++++ .../core/src/Foundation/AbstractValidator.php | 17 ++++ .../core/src/Foundation/Event/Validating.php | 1 + .../integration/extenders/ValidatorTest.php | 97 +++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 framework/core/src/Extend/Validator.php create mode 100644 framework/core/tests/integration/extenders/ValidatorTest.php diff --git a/framework/core/src/Extend/Validator.php b/framework/core/src/Extend/Validator.php new file mode 100644 index 000000000..62301e93c --- /dev/null +++ b/framework/core/src/Extend/Validator.php @@ -0,0 +1,58 @@ +validator = $validatorClass; + } + + /** + * Configure the validator. This is often used to adjust validation rules, but can be + * used to make other changes to the validator as well. + * + * @param callable $callable + * + * The callable can be a closure or invokable class, and should accept: + * - \Flarum\Foundation\AbstractValidator $flarumValidator: The Flarum validator wrapper + * - \Illuminate\Validation\Validator $validator: The Laravel validator instance + */ + public function configure($callback) + { + $this->configurationCallbacks[] = $callback; + + return $this; + } + + public function extend(Container $container, Extension $extension = null) + { + $container->resolving($this->validator, function ($validator, $container) { + foreach ($this->configurationCallbacks as $callback) { + if (is_string($callback)) { + $callback = $container->make($callback); + } + + $validator->addConfiguration($callback); + } + }); + } +} diff --git a/framework/core/src/Foundation/AbstractValidator.php b/framework/core/src/Foundation/AbstractValidator.php index 37fff96bd..980558337 100644 --- a/framework/core/src/Foundation/AbstractValidator.php +++ b/framework/core/src/Foundation/AbstractValidator.php @@ -18,6 +18,16 @@ use Symfony\Component\Translation\TranslatorInterface; abstract class AbstractValidator { + /** + * @var array + */ + protected $configuration = []; + + public function addConfiguration($callable) + { + $this->configuration[] = $callable; + } + /** * @var array */ @@ -92,10 +102,17 @@ abstract class AbstractValidator $validator = $this->validator->make($attributes, $rules, $this->getMessages()); + /** + * @deprecated in beta 15, removed in beta 16. + */ $this->events->dispatch( new Validating($this, $validator) ); + foreach ($this->configuration as $callable) { + $callable($this, $validator); + } + return $validator; } } diff --git a/framework/core/src/Foundation/Event/Validating.php b/framework/core/src/Foundation/Event/Validating.php index c8a6befcb..86c6d4cd3 100644 --- a/framework/core/src/Foundation/Event/Validating.php +++ b/framework/core/src/Foundation/Event/Validating.php @@ -13,6 +13,7 @@ use Flarum\Foundation\AbstractValidator; use Illuminate\Validation\Validator; /** + * @deprecated in Beta 15, remove in beta 16. Use the Validator extender instead. * The `Validating` event is called when a validator instance for a * model is being built. This event can be used to add custom rules/extensions * to the validator for when validation takes place. diff --git a/framework/core/tests/integration/extenders/ValidatorTest.php b/framework/core/tests/integration/extenders/ValidatorTest.php new file mode 100644 index 000000000..6a93e8bb6 --- /dev/null +++ b/framework/core/tests/integration/extenders/ValidatorTest.php @@ -0,0 +1,97 @@ +extend((new Extend\Validator(UserValidator::class))->configure(function ($flarumValidator, $validator) { + $validator->setRules([ + 'password' => [ + 'required', + 'min:20' + ] + ] + $validator->getRules()); + })); + } + + private function extendToRequireLongPasswordViaInvokableClass() + { + $this->extend((new Extend\Validator(UserValidator::class))->configure(CustomValidatorClass::class)); + } + + /** + * @test + */ + public function custom_validation_rule_does_not_exist_by_default() + { + $this->app()->getContainer()->make(UserValidator::class)->assertValid(['password' => 'simplePassword']); + + // If we have gotten this far, no validation exception has been thrown, so the test is succesful. + $this->assertTrue(true); + } + + /** + * @test + */ + public function custom_validation_rule_exists_if_added() + { + $this->extendToRequireLongPassword(); + + $this->expectException(ValidationException::class); + + $this->app()->getContainer()->make(UserValidator::class)->assertValid(['password' => 'simplePassword']); + } + + /** + * @test + */ + public function custom_validation_rule_exists_if_added_via_invokable_class() + { + $this->extendToRequireLongPasswordViaInvokableClass(); + + $this->expectException(ValidationException::class); + + $this->app()->getContainer()->make(UserValidator::class)->assertValid(['password' => 'simplePassword']); + } + + /** + * @test + */ + public function custom_validation_rule_doesnt_affect_other_validators() + { + $this->extendToRequireLongPassword(); + + $this->app()->getContainer()->make(GroupValidator::class)->assertValid(['password' => 'simplePassword']); + + // If we have gotten this far, no validation exception has been thrown, so the test is succesful. + $this->assertTrue(true); + } +} + +class CustomValidatorClass +{ + public function __invoke($flarumValidator, $validator) + { + $validator->setRules([ + 'password' => [ + 'required', + 'min:20' + ] + ] + $validator->getRules()); + } +}