From 5ffc0b69a87f815a55cc3b3a304c2e756cb30469 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Sep 2023 10:14:46 +0900 Subject: [PATCH 1/3] feat: array callable Validation rule If a rule is a callable, there is no way to set param. So the removed `($param === false)` is always true. $passed = $param === false ? $rule($value) : $rule($value, $param, $data); --- system/Validation/Validation.php | 14 +++- tests/system/Validation/ValidationTest.php | 95 ++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index b40bc91048bc..ef98496ac370 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -278,7 +278,9 @@ protected function processRules( } foreach ($rules as $i => $rule) { - $isCallable = is_callable($rule); + $isCallable = is_callable($rule); + $stringCallable = ($isCallable && is_string($rule)) ? true : false; + $arrayCallable = ($isCallable && is_array($rule)) ? true : false; $passed = false; $param = false; @@ -295,7 +297,13 @@ protected function processRules( if ($this->isClosure($rule)) { $passed = $rule($value, $data, $error, $field); } elseif ($isCallable) { - $passed = $param === false ? $rule($value) : $rule($value, $param, $data); + if ($stringCallable) { + // The rule is a function. + $passed = $rule($value); + } else { + // The rule is an array. + $passed = $rule($value, $data, $error, $field); + } } else { $found = false; @@ -335,7 +343,7 @@ protected function processRules( // @phpstan-ignore-next-line $error may be set by rule methods. $this->errors[$field] = $error ?? $this->getErrorMessage( - $this->isClosure($rule) ? $i : $rule, + ($this->isClosure($rule) || $arrayCallable) ? $i : $rule, $field, $label, $param, diff --git a/tests/system/Validation/ValidationTest.php b/tests/system/Validation/ValidationTest.php index e015c8b21de9..b3fce0d7c455 100644 --- a/tests/system/Validation/ValidationTest.php +++ b/tests/system/Validation/ValidationTest.php @@ -343,6 +343,101 @@ public function testClosureRuleWithLabel(): void ); } + /** + * Validation rule1 + * + * @param mixed $value + */ + public function rule1($value) + { + return $value === 'abc'; + } + + public function testCallableRule(): void + { + $this->validation->setRules( + [ + 'foo' => ['required', [$this, 'rule1']], + ], + [ + // Errors + 'foo' => [ + // Specify the array key for the callable rule. + 1 => 'The value is not "abc"', + ], + ], + ); + + $data = ['foo' => 'xyz']; + $result = $this->validation->run($data); + + $this->assertFalse($result); + $this->assertSame( + ['foo' => 'The value is not "abc"'], + $this->validation->getErrors() + ); + $this->assertSame([], $this->validation->getValidated()); + } + + /** + * Validation rule1 + * + * @param mixed $value + */ + public function rule2($value, array $data, ?string &$error, string $field) + { + if ($value !== 'abc') { + $error = 'The ' . $field . ' value is not "abc"'; + + return false; + } + + return true; + } + + public function testCallableRuleWithParamError(): void + { + $this->validation->setRules([ + 'foo' => [ + 'required', + [$this, 'rule2'], + ], + ]); + + $data = ['foo' => 'xyz']; + $result = $this->validation->run($data); + + $this->assertFalse($result); + $this->assertSame( + ['foo' => 'The foo value is not "abc"'], + $this->validation->getErrors() + ); + $this->assertSame([], $this->validation->getValidated()); + } + + public function testCallableRuleWithLabel(): void + { + $this->validation->setRules([ + 'secret' => [ + 'label' => 'シークレット', + 'rules' => ['required', [$this, 'rule1']], + 'errors' => [ + // Specify the array key for the callable rule. + 1 => 'The {field} is invalid', + ], + ], + ]); + + $data = ['secret' => 'xyz']; + $result = $this->validation->run($data); + + $this->assertFalse($result); + $this->assertSame( + ['secret' => 'The シークレット is invalid'], + $this->validation->getErrors() + ); + } + /** * @see https://github.com/codeigniter4/CodeIgniter4/issues/5368 * From 91f8b4b8bd9ba63a9eb683fc95bdfc851f09d9f0 Mon Sep 17 00:00:00 2001 From: kenjis Date: Wed, 13 Sep 2023 11:11:40 +0900 Subject: [PATCH 2/3] docs: add and update docs --- .../installation/upgrade_validations.rst | 13 +++--- .../source/libraries/validation.rst | 23 ++++++++++ .../source/libraries/validation/046.php | 43 +++++++++++++++++++ .../source/libraries/validation/047.php | 22 ++++++++++ 4 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 user_guide_src/source/libraries/validation/046.php create mode 100644 user_guide_src/source/libraries/validation/047.php diff --git a/user_guide_src/source/installation/upgrade_validations.rst b/user_guide_src/source/installation/upgrade_validations.rst index 8343b989a699..b45823716869 100644 --- a/user_guide_src/source/installation/upgrade_validations.rst +++ b/user_guide_src/source/installation/upgrade_validations.rst @@ -14,12 +14,15 @@ Documentations of Library What has been changed ===================== - If you want to change validation error display, you have to set CI4 :ref:`validation View templates `. -- CI4 validation has no Callbacks nor Callable in CI3. - Use :ref:`Rule Classes ` or - :ref:`Closure Rule ` - instead. -- In CI3, Callbacks/Callable rules were prioritized, but in CI4, Closure Rules are +- CI4 validation has no `Callbacks `_ + in CI3. + Use :ref:`Callable Rules ` (since v4.5.0) or + :ref:`Closure Rules ` (since v4.3.0) or + :ref:`Rule Classes ` instead. +- In CI3, Callbacks/Callable rules were prioritized, but in CI4, Closure/Callable Rules are not prioritized, and are checked in the order in which they are listed. +- Since v4.5.0, :ref:`Callable Rules ` has been + introduced, but it is a bit different from CI3's `Callable `_. - CI4 validation format rules do not permit empty string. - CI4 validation never changes your data. - Since v4.3.0, :php:func:`validation_errors()` has been introduced, but the API is different from CI3's. diff --git a/user_guide_src/source/libraries/validation.rst b/user_guide_src/source/libraries/validation.rst index 3224961e0821..2f3cdd662d70 100644 --- a/user_guide_src/source/libraries/validation.rst +++ b/user_guide_src/source/libraries/validation.rst @@ -794,6 +794,29 @@ Or you can use the following parameters: .. literalinclude:: validation/041.php :lines: 2- +.. _validation-using-callable-rule: + +Using Callable Rule +=================== + +.. versionadded:: 4.5.0 + +If you like to use an array callback as a rule, you may use it instead of a Closure Rule. + +You need to use an array for validation rules: + +.. literalinclude:: validation/046.php + :lines: 2- + +You must set the error message for the callable rule. +When you specify the error message, set the array key for the callable rule. +In the above code, the ``required`` rule has the key ``0``, and the callable has ``1``. + +Or you can use the following parameters: + +.. literalinclude:: validation/047.php + :lines: 2- + *************** Available Rules *************** diff --git a/user_guide_src/source/libraries/validation/046.php b/user_guide_src/source/libraries/validation/046.php new file mode 100644 index 000000000000..f37def009eba --- /dev/null +++ b/user_guide_src/source/libraries/validation/046.php @@ -0,0 +1,43 @@ +setRules( + [ + 'foo' => [ + 'required', + // Specify the method in this controller as a rule. + [$this, '_ruleEven'], + ], + ], + [ + // Errors + 'foo' => [ + // Specify the array key for the callable rule. + 1 => 'The value is not even.', + ], + ], + ); + + if (! $validation->run($data)) { + // handle validation errors + } + + // ... + } +} diff --git a/user_guide_src/source/libraries/validation/047.php b/user_guide_src/source/libraries/validation/047.php new file mode 100644 index 000000000000..6c2bb674f800 --- /dev/null +++ b/user_guide_src/source/libraries/validation/047.php @@ -0,0 +1,22 @@ + Date: Wed, 13 Sep 2023 11:40:16 +0900 Subject: [PATCH 3/3] refactor: by rector --- system/Validation/Validation.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/system/Validation/Validation.php b/system/Validation/Validation.php index ef98496ac370..b42e093014fe 100644 --- a/system/Validation/Validation.php +++ b/system/Validation/Validation.php @@ -279,8 +279,8 @@ protected function processRules( foreach ($rules as $i => $rule) { $isCallable = is_callable($rule); - $stringCallable = ($isCallable && is_string($rule)) ? true : false; - $arrayCallable = ($isCallable && is_array($rule)) ? true : false; + $stringCallable = $isCallable && is_string($rule); + $arrayCallable = $isCallable && is_array($rule); $passed = false; $param = false; @@ -297,13 +297,7 @@ protected function processRules( if ($this->isClosure($rule)) { $passed = $rule($value, $data, $error, $field); } elseif ($isCallable) { - if ($stringCallable) { - // The rule is a function. - $passed = $rule($value); - } else { - // The rule is an array. - $passed = $rule($value, $data, $error, $field); - } + $passed = $stringCallable ? $rule($value) : $rule($value, $data, $error, $field); } else { $found = false;