Skip to content

Commit

Permalink
feat: array callable Validation rule
Browse files Browse the repository at this point in the history
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);
  • Loading branch information
kenjis committed Sep 23, 2023
1 parent e6015fa commit 5ffc0b6
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
14 changes: 11 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)) ? true : false;
$arrayCallable = ($isCallable && is_array($rule)) ? true : false;

$passed = false;
$param = false;
Expand All @@ -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;

Expand Down Expand Up @@ -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,
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

0 comments on commit 5ffc0b6

Please sign in to comment.