Skip to content

Commit 82ad1be

Browse files
authored
Merge pull request #22 from rakit/rule-callback
Adding Rule Callback
2 parents 4d346c9 + 3ad71fb commit 82ad1be

File tree

6 files changed

+149
-9
lines changed

6 files changed

+149
-9
lines changed

README.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ Below is list of all available validation rules
293293
* [different](#rule-different)
294294
* [after](#after)
295295
* [before](#before)
296+
* [callback](#callback)
296297

297298
<a id="rule-required"></a>
298299
#### required
@@ -489,10 +490,54 @@ Anything that can be parsed by `strtotime` can be passed as a parameter to this
489490

490491
This also works the same way as the [after rule](#after). Pass anything that can be parsed by `strtotime`
491492

493+
<a id="callback"></a>
494+
#### callback
495+
496+
You can use this rule to define your own validation rule.
497+
This rule can't be registered using string pipe.
498+
To use this rule, you should put Closure inside array of rules.
499+
500+
For example:
501+
502+
```php
503+
$validation = $validator->validate($_POST, [
504+
'even_number' => [
505+
'required',
506+
function ($value) {
507+
// false = invalid
508+
return (is_numeric($value) AND $value % 2 === 0);
509+
}
510+
]
511+
]);
512+
```
513+
514+
You can set invalid message by returning a string.
515+
For example, example above would be:
516+
517+
```php
518+
$validation = $validator->validate($_POST, [
519+
'even_number' => [
520+
'required',
521+
function ($value) {
522+
if (!is_numeric($value)) {
523+
return ":attribute must be numeric.";
524+
}
525+
if ($value % 2 !== 0) {
526+
return ":attribute is not even number.";
527+
}
528+
// you can return true or don't return anything if value is valid
529+
}
530+
]
531+
]);
532+
```
533+
534+
> Note: `Rakit\Validation\Rules\Callback` instance is binded into your Closure.
535+
So you can access rule properties and methods using `$this`.
536+
492537
## Register/Modify Rule
493538

494-
To create your own validation rule, you need to create a class extending `Rakit\Validation\Rule`
495-
then register it using `setValidator` or `addValidator`.
539+
Another way to use custom validation rule is to create a class extending `Rakit\Validation\Rule`.
540+
Then register it using `setValidator` or `addValidator`.
496541

497542
For example, you want to create `unique` validator that check field availability from database.
498543

src/Rules/Callback.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Rakit\Validation\Rules;
4+
5+
use Rakit\Validation\Rule;
6+
use InvalidArgumentException;
7+
use Closure;
8+
9+
class Callback extends Rule
10+
{
11+
12+
protected $message = "The :attribute is not valid";
13+
14+
protected $fillable_params = ['callback'];
15+
16+
public function setCallback(Closure $callback)
17+
{
18+
return $this->setParameter('callback', $callback);
19+
}
20+
21+
public function check($value)
22+
{
23+
$this->requireParameters($this->fillable_params);
24+
25+
$callback = $this->parameter('callback');
26+
if (false === $callback instanceof Closure) {
27+
$key = $this->attribute->getKey();
28+
throw new InvalidArgumentException("Callback rule for '{$key}' is not callable.");
29+
}
30+
31+
$callback = $callback->bindTo($this);
32+
$invalidMessage = $callback($value);
33+
34+
if (is_string($invalidMessage)) {
35+
$this->setMessage($invalidMessage);
36+
return false;
37+
} elseif(false === $invalidMessage) {
38+
return false;
39+
}
40+
41+
return true;
42+
}
43+
44+
}

src/Validation.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Rakit\Validation;
44

55
use Rakit\Validation\Rules\Required;
6+
use Closure;
67

78
class Validation
89
{
@@ -326,12 +327,13 @@ protected function resolveRules($rules)
326327
$validator = call_user_func_array($validatorFactory, array_merge([$rulename], $params));
327328
} elseif($rule instanceof Rule) {
328329
$validator = $rule;
330+
} elseif($rule instanceof Closure) {
331+
$validator = call_user_func_array($validatorFactory, ['callback', $rule]);
329332
} else {
330333
$ruleName = is_object($rule) ? get_class($rule) : gettype($rule);
331-
throw new \Exception("Rule must be a string or Rakit\Validation\Rule instance. ".$ruleName." given", 1);
334+
throw new \Exception("Rule must be a string, closure or Rakit\Validation\Rule instance. ".$ruleName." given");
332335
}
333336

334-
335337
$resolved_rules[] = $validator;
336338
}
337339

src/Validator.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22

33
namespace Rakit\Validation;
44

5-
use Rakit\Validation\Rules\After;
6-
use Rakit\Validation\Rules\Before;
7-
85
class Validator
96
{
107

@@ -102,8 +99,9 @@ protected function registerBaseValidators()
10299
'present' => new Rules\Present,
103100
'different' => new Rules\Different,
104101
'uploaded_file' => new Rules\UploadedFile,
105-
'before' => new Before,
106-
'after' => new After
102+
'callback' => new Rules\Callback,
103+
'before' => new Rules\Before,
104+
'after' => new Rules\After,
107105
];
108106

109107
foreach($baseValidator as $key => $validator) {

tests/Rules/CallbackTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use Rakit\Validation\Rules\Callback;
4+
5+
class CallbackTest extends PHPUnit_Framework_TestCase
6+
{
7+
8+
public function setUp()
9+
{
10+
$this->rule = new Callback;
11+
$this->rule->setCallback(function($value) {
12+
return (is_numeric($value) AND $value % 2 === 0);
13+
});
14+
}
15+
16+
public function testValids()
17+
{
18+
$this->assertTrue($this->rule->check(2));
19+
$this->assertTrue($this->rule->check('4'));
20+
$this->assertTrue($this->rule->check("1000"));
21+
}
22+
23+
public function testInvalids()
24+
{
25+
$this->assertFalse($this->rule->check(1));
26+
$this->assertFalse($this->rule->check('abc12'));
27+
$this->assertFalse($this->rule->check("12abc"));
28+
}
29+
30+
}

tests/ValidatorTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,27 @@ public function testSetCustomMessagesInValidation()
626626
$this->assertEquals($errors->first('comments.0.text:required'), 'baz');
627627
}
628628

629+
public function testCustomMessageInCallbackRule()
630+
{
631+
$evenNumberValidator = function ($value) {
632+
if (!is_numeric($value) OR $value % 2 !== 0) {
633+
return ":attribute must be even number";
634+
}
635+
return true;
636+
};
637+
638+
$validation = $this->validator->make([
639+
'foo' => 'abc',
640+
], [
641+
'foo' => [$evenNumberValidator],
642+
]);
643+
644+
$validation->validate();
645+
646+
$errors = $validation->errors();
647+
$this->assertEquals($errors->first('foo:callback'), "Foo must be even number");
648+
}
649+
629650
public function testSpecificRuleMessage()
630651
{
631652
$validation = $this->validator->make([

0 commit comments

Comments
 (0)