Skip to content

Commit b3fe111

Browse files
committed
Add postal_code validator extension
1 parent fa95fa1 commit b3fe111

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Worldwide postal code validation for Laravel, based on Google's Address Data Ser
2222
- [Usage](#usage)
2323
- [Available rules](#available-rules)
2424
- [Adding an error message](#adding-an-error-message)
25+
- [String rules](#string-rules)
2526
- [Changelog](#changelog)
2627
- [Contributing](#contributing)
2728
- [Credits](#credits)
@@ -111,6 +112,19 @@ The following placeholders will be automatically filled for you:
111112

112113
*The `:countries` and `:examples` placeholders may be empty if no valid countries are passed.
113114

115+
### String rules
116+
117+
All rules provided by this package can also be used as strings. This is especially useful when validating arrays, for
118+
example:
119+
120+
```php
121+
$request->validate([
122+
'addresses' => '...',
123+
'addresses.*.country' => '...',
124+
'addresses.*.postal_code' => 'postal_code:addresses.*.country',
125+
]);
126+
```
127+
114128
## Changelog
115129

116130
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

src/Support/ValidationServiceProvider.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66

77
use Axlon\PostalCodeValidation\Contracts\RegexFactory;
88
use Axlon\PostalCodeValidation\Regex\RegexRepository;
9+
use Axlon\PostalCodeValidation\Rules\PostalCode;
10+
use Closure;
911
use Illuminate\Support\ServiceProvider;
12+
use Illuminate\Validation\Factory;
13+
use Illuminate\Validation\InvokableValidationRule;
14+
use Illuminate\Validation\Validator;
1015

1116
final class ValidationServiceProvider extends ServiceProvider
1217
{
@@ -18,5 +23,31 @@ final class ValidationServiceProvider extends ServiceProvider
1823
public function register(): void
1924
{
2025
$this->app->singleton(RegexFactory::class, RegexRepository::class);
26+
27+
$this->callAfterResolving('validator', static function (Factory $validator) {
28+
$validator->extendDependent('postal_code', self::createExtension(PostalCode::of(...)));
29+
});
30+
}
31+
32+
/**
33+
* Create a validator extension.
34+
*
35+
* @param \Closure(array<string>): \Illuminate\Contracts\Validation\ValidationRule $callback
36+
* @return \Closure(string, mixed, array<string>, \Illuminate\Validation\Validator): bool
37+
*/
38+
private static function createExtension(Closure $callback): Closure
39+
{
40+
return static function (
41+
string $attribute,
42+
mixed $value,
43+
array $parameters,
44+
Validator $validator,
45+
) use ($callback): bool {
46+
/** @var array<string> $parameters */
47+
$invokable = InvokableValidationRule::make($callback($parameters));
48+
$invokable->setValidator($validator);
49+
50+
return $invokable->passes($attribute, $value);
51+
};
2152
}
2253
}

tests/Support/ValidationServiceProviderTest.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,17 @@
44

55
namespace Tests\Support;
66

7+
use Axlon\PostalCodeValidation\Contracts\Regex;
78
use Axlon\PostalCodeValidation\Contracts\RegexFactory;
9+
use Axlon\PostalCodeValidation\Rules\PostalCode;
810
use Axlon\PostalCodeValidation\Support\ValidationServiceProvider;
11+
use Illuminate\Support\Facades\Validator;
912
use Orchestra\Testbench\TestCase;
1013
use PHPUnit\Framework\Attributes\CoversClass;
14+
use PHPUnit\Framework\Attributes\UsesClass;
1115

1216
#[CoversClass(ValidationServiceProvider::class)]
17+
#[UsesClass(PostalCode::class)]
1318
final class ValidationServiceProviderTest extends TestCase
1419
{
1520
protected function getPackageProviders($app): array
@@ -26,4 +31,35 @@ public function testItBindsRegexFactory(): void
2631
self::assertInstanceOf(RegexFactory::class, $regexes);
2732
self::assertSame($regexes, $this->app?->make(RegexFactory::class));
2833
}
34+
35+
public function testItCreatesPostalCodeExtensionThatFailsWhenRegexDoesNotMatch(): void
36+
{
37+
$regex = self::createMock(Regex::class);
38+
$regex->method('test')->with('1234 AB')->willReturn(false);
39+
40+
$this->mock(RegexFactory::class)
41+
->expects('get')
42+
->with('NL')
43+
->andReturns($regex);
44+
45+
$validator = Validator::make(['value' => '1234 AB'], ['value' => 'postal_code:NL']);
46+
47+
self::assertFalse($validator->passes());
48+
self::assertSame(['validation.postal_code'], $validator->errors()->get('value'));
49+
}
50+
51+
public function testItCreatesPostalCodeExtensionThatPassesWhenRegexMatches(): void
52+
{
53+
$regex = self::createMock(Regex::class);
54+
$regex->method('test')->with('1234 AB')->willReturn(true);
55+
56+
$this->mock(RegexFactory::class)
57+
->expects('get')
58+
->with('NL')
59+
->andReturns($regex);
60+
61+
$validator = Validator::make(['value' => '1234 AB'], ['value' => 'postal_code:NL']);
62+
63+
self::assertTrue($validator->passes());
64+
}
2965
}

0 commit comments

Comments
 (0)