Skip to content

Commit

Permalink
Merge pull request #7933 from kenjis/feat-validation-callable
Browse files Browse the repository at this point in the history
feat: [Validation] Callable Rules
  • Loading branch information
kenjis authored Oct 1, 2023
2 parents d40c071 + a089af6 commit 307da97
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 8 deletions.
8 changes: 5 additions & 3 deletions system/Validation/Validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
$arrayCallable = $isCallable && is_array($rule);

$passed = false;
$param = false;
Expand All @@ -295,7 +297,7 @@ protected function processRules(
if ($this->isClosure($rule)) {
$passed = $rule($value, $data, $error, $field);
} elseif ($isCallable) {
$passed = $param === false ? $rule($value) : $rule($value, $param, $data);
$passed = $stringCallable ? $rule($value) : $rule($value, $data, $error, $field);
} else {
$found = false;

Expand Down Expand Up @@ -335,7 +337,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,
Expand Down
95 changes: 95 additions & 0 deletions tests/system/Validation/ValidationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
13 changes: 8 additions & 5 deletions user_guide_src/source/installation/upgrade_validations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <validation-customizing-error-display>`.
- CI4 validation has no Callbacks nor Callable in CI3.
Use :ref:`Rule Classes <validation-using-rule-classes>` or
:ref:`Closure Rule <validation-using-closure-rule>`
instead.
- In CI3, Callbacks/Callable rules were prioritized, but in CI4, Closure Rules are
- CI4 validation has no `Callbacks <http://www.codeigniter.com/userguide3/libraries/form_validation.html#callbacks-your-own-validation-methods>`_
in CI3.
Use :ref:`Callable Rules <validation-using-callable-rule>` (since v4.5.0) or
:ref:`Closure Rules <validation-using-closure-rule>` (since v4.3.0) or
:ref:`Rule Classes <validation-using-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 <validation-using-callable-rule>` has been
introduced, but it is a bit different from CI3's `Callable <http://www.codeigniter.com/userguide3/libraries/form_validation.html#callable-use-anything-as-a-rule>`_.
- 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.
Expand Down
23 changes: 23 additions & 0 deletions user_guide_src/source/libraries/validation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,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
***************
Expand Down
43 changes: 43 additions & 0 deletions user_guide_src/source/libraries/validation/046.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace App\Controllers;

use Config\Services;

class Form extends BaseController
{
// Define a custom validation rule.
public function _ruleEven($value): bool
{
return (int) $value % 2 === 0;
}

public function process()
{
// ...

$validation = Services::validation();
$validation->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
}

// ...
}
}
22 changes: 22 additions & 0 deletions user_guide_src/source/libraries/validation/047.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Controllers;

use Config\Services;

class Form extends BaseController
{
// Define a custom validation rule.
public function _ruleEven($value, $data, &$error, $field): bool
{
if ((int) $value % 2 === 0) {
return true;
}

$error = 'The value is not even.';

return false;
}

// ...
}

0 comments on commit 307da97

Please sign in to comment.