Skip to content

Commit 90e684c

Browse files
committed
First working version
1 parent b75556d commit 90e684c

3 files changed

Lines changed: 144 additions & 14 deletions

File tree

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ For validating a zip code you need to instantiate a new ZipCode class provided b
2727
$form = $this->createFormBuilder($address)
2828
->add('zipcode', TextType::class, [
2929
'constraints' => [
30-
new ZipCodeValidator\Constraints\ZipCode([
31-
'iso' => 'DE'
32-
])
30+
new ZipCodeValidator\Constraints\ZipCode(iso: 'DE')
3331
]
3432
])
3533
->add('save', SubmitType::class, ['label' => 'Create Task'])
@@ -51,19 +49,24 @@ class Address
5149
}
5250
```
5351

54-
You can also use it as a PHP8 Attribute, with parameters passed as an array of options, for example:
52+
You can also use it as a PHP8 Attribute with named parameters:
5553
```php
5654
<?php
5755

5856
use ZipCodeValidator\Constraints\ZipCode;
5957

6058
class Address
6159
{
62-
#[ZipCode(['iso'=>'DE'])
60+
#[ZipCode(iso: 'DE')]
6361
protected $zipCode;
6462
}
6563
```
6664

65+
Legacy array options are still supported for backward compatibility:
66+
```php
67+
#[ZipCode(['iso' => 'DE'])]
68+
```
69+
6770
> Please consider to inject a valid ISO 3166 2-letter country code (e.g. DE, US, FR)!
6871
6972
> NOTE: This library validates against known zip code regex patterns and does not validate the existence of a zipcode.
@@ -117,10 +120,7 @@ protected $zipCode;
117120
### Case insensitive zip code matching
118121
In case you want to match the zip code in a case insensitive way you have to pass a `caseSensitiveCheck` parameter with `false` value via the constructor:
119122
```php
120-
$constraint = new ZipCode([
121-
'iso' => 'GB',
122-
'caseSensitiveCheck' => false
123-
]);
123+
$constraint = new ZipCode(iso: 'GB', caseSensitiveCheck: false);
124124

125125
```
126126
By the default the library is using case sensitive zip code matching.

src/ZipCodeValidator/Constraints/ZipCode.php

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Attribute;
66
use Symfony\Component\Validator\Constraint;
7+
use Symfony\Component\Validator\Exception\InvalidOptionsException;
78
use Symfony\Component\Validator\Exception\MissingOptionsException;
89

910
/**
@@ -20,15 +21,81 @@ class ZipCode extends Constraint
2021
public bool $strict = true;
2122
public bool $caseSensitiveCheck = true;
2223

23-
public function __construct(mixed $options = null, ?array $groups = null, mixed $payload = null)
24+
public function __construct(
25+
mixed $options = null,
26+
?array $groups = null,
27+
mixed $payload = null,
28+
?string $iso = null,
29+
?string $getter = null,
30+
?bool $strict = null,
31+
?bool $caseSensitiveCheck = null,
32+
?string $message = null
33+
)
2434
{
2535
if (is_string($options)) {
26-
$options = array(
27-
'iso' => $options
36+
$options = [
37+
'iso' => $options,
38+
];
39+
} elseif (null === $options) {
40+
$options = [];
41+
} elseif (!is_array($options)) {
42+
throw new InvalidOptionsException(sprintf('The options "%s" do not exist in constraint "%s".', 'options', __CLASS__), ['options']);
43+
}
44+
45+
$availableOptions = ['iso', 'getter', 'strict', 'caseSensitiveCheck', 'message', 'groups', 'payload'];
46+
$invalidOptions = array_values(array_filter(array_keys($options), fn ($option) => !in_array($option, $availableOptions, true)));
47+
if ([] !== $invalidOptions) {
48+
throw new InvalidOptionsException(
49+
sprintf('The options "%s" do not exist in constraint "%s".', implode('", "', $invalidOptions), __CLASS__),
50+
$invalidOptions
2851
);
2952
}
3053

31-
parent::__construct($options, $groups, $payload);
54+
$resolvedOptions = [
55+
'iso' => $iso,
56+
'getter' => $getter,
57+
'strict' => $strict,
58+
'caseSensitiveCheck' => $caseSensitiveCheck,
59+
'message' => $message,
60+
'groups' => $groups,
61+
'payload' => $payload,
62+
];
63+
64+
foreach ($resolvedOptions as $option => $resolvedValue) {
65+
if (null !== $resolvedValue || !array_key_exists($option, $options)) {
66+
continue;
67+
}
68+
69+
$resolvedOptions[$option] = 'groups' === $option ? (array) $options[$option] : $options[$option];
70+
}
71+
72+
$iso = $resolvedOptions['iso'];
73+
$getter = $resolvedOptions['getter'];
74+
$strict = $resolvedOptions['strict'];
75+
$caseSensitiveCheck = $resolvedOptions['caseSensitiveCheck'];
76+
$message = $resolvedOptions['message'];
77+
78+
parent::__construct(null, $resolvedOptions['groups'], $resolvedOptions['payload']);
79+
80+
if (null !== $iso) {
81+
$this->iso = $iso;
82+
}
83+
84+
if (null !== $getter) {
85+
$this->getter = $getter;
86+
}
87+
88+
if (null !== $strict) {
89+
$this->strict = $strict;
90+
}
91+
92+
if (null !== $caseSensitiveCheck) {
93+
$this->caseSensitiveCheck = $caseSensitiveCheck;
94+
}
95+
96+
if (null !== $message) {
97+
$this->message = $message;
98+
}
3299

33100
if (null === $this->iso && null === $this->getter) {
34101
throw new MissingOptionsException(sprintf('Either the option "iso" or "getter" must be given for constraint %s', __CLASS__), ['iso', 'getter']);

tests/Constraints/ZipCodeTest.php

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace ZipCodeValidator\Tests\Constraints;
44

55
use PHPUnit\Framework\TestCase;
6+
use Symfony\Component\Validator\Exception\InvalidOptionsException;
67
use Symfony\Component\Validator\Exception\MissingOptionsException;
78
use ZipCodeValidator\Constraints\ZipCode;
89

@@ -13,4 +14,66 @@ public function testMissingOptionsExceptionWhenIsoAndGetterIsEmpty(): void
1314
$this->expectException(MissingOptionsException::class);
1415
$constraint = new ZipCode(null);
1516
}
16-
}
17+
18+
public function testLegacyStringOptionSetsIso(): void
19+
{
20+
$constraint = new ZipCode('DE');
21+
22+
$this->assertSame('DE', $constraint->iso);
23+
}
24+
25+
public function testLegacyArrayOptionsAreStillSupported(): void
26+
{
27+
$payload = new \stdClass();
28+
$constraint = new ZipCode([
29+
'iso' => 'GB',
30+
'strict' => false,
31+
'caseSensitiveCheck' => false,
32+
'message' => 'Custom message',
33+
'groups' => 'Address',
34+
'payload' => $payload,
35+
]);
36+
37+
$this->assertSame('GB', $constraint->iso);
38+
$this->assertFalse($constraint->strict);
39+
$this->assertFalse($constraint->caseSensitiveCheck);
40+
$this->assertSame('Custom message', $constraint->message);
41+
$this->assertSame(['Address'], $constraint->groups);
42+
$this->assertSame($payload, $constraint->payload);
43+
}
44+
45+
public function testNamedParametersAreSupported(): void
46+
{
47+
$constraint = new ZipCode(
48+
iso: 'FR',
49+
strict: false,
50+
caseSensitiveCheck: false,
51+
message: 'Another message',
52+
groups: ['Checkout']
53+
);
54+
55+
$this->assertSame('FR', $constraint->iso);
56+
$this->assertFalse($constraint->strict);
57+
$this->assertFalse($constraint->caseSensitiveCheck);
58+
$this->assertSame('Another message', $constraint->message);
59+
$this->assertSame(['Checkout'], $constraint->groups);
60+
}
61+
62+
public function testNamedParametersTakePrecedenceOverLegacyOptionsArray(): void
63+
{
64+
$constraint = new ZipCode(
65+
['iso' => 'DE', 'strict' => true],
66+
iso: 'US',
67+
strict: false
68+
);
69+
70+
$this->assertSame('US', $constraint->iso);
71+
$this->assertFalse($constraint->strict);
72+
}
73+
74+
public function testUnknownLegacyOptionThrowsException(): void
75+
{
76+
$this->expectException(InvalidOptionsException::class);
77+
new ZipCode(['foo' => 'bar', 'iso' => 'FR']);
78+
}
79+
}

0 commit comments

Comments
 (0)