Skip to content

Commit 9d597b7

Browse files
authored
Merge pull request #196 from sumocoders/39-autocomplete-postcodes
BelgiumPostCode form type
2 parents 4d93b7b + c22d32c commit 9d597b7

File tree

11 files changed

+2915
-1
lines changed

11 files changed

+2915
-1
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"doctrine/doctrine-bundle": "^2.13",
3838
"doctrine/orm": "^3.3",
3939
"symfony/doctrine-messenger": "^7.3",
40+
"symfony/intl": "^7.3",
4041
"symfony/ux-toggle-password": "^2.22"
4142
},
4243
"require-dev": {

config/services.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
declare(strict_types=1);
44

5-
use SumoCoders\FrameworkCoreBundle\Command\SecretsGetCommand;
65
use SumoCoders\FrameworkCoreBundle\Command\TranslateCommand;
76
use SumoCoders\FrameworkCoreBundle\DoctrineListener\DoctrineAuditListener;
87
use SumoCoders\FrameworkCoreBundle\EventListener\TitleListener;
8+
use SumoCoders\FrameworkCoreBundle\Form\Type\BelgiumPostCodeType;
99
use SumoCoders\FrameworkCoreBundle\Logger\AuditLogger;
1010
use SumoCoders\FrameworkCoreBundle\Service\PageTitle;
1111
use SumoCoders\FrameworkCoreBundle\Twig\ContentExtension;
@@ -84,6 +84,9 @@
8484
->set('framework.file_type', FileType::class)
8585
->tag('form.type', ['alias' => 'sumoFile'])
8686

87+
->set('framework.file_type', BelgiumPostCodeType::class)
88+
->tag('form.type', ['alias' => 'sumoBelgiumPostCode'])
89+
8790
/*
8891
* Secure headers
8992
*/

docs/development/forms.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,18 @@ Where `Username` is a translation. So if you want to translate the label, you ca
2929
```yaml
3030
Username: 'Enter your username'
3131
```
32+
33+
## Belgium postcode
34+
35+
Form type that allows only valid belgian postcodes to be selected.
36+
37+
Use in your own form:
38+
```php
39+
$builder->add('postcode', BelgiumPostCodeType::class);
40+
```
41+
42+
Add a property in the DTO:
43+
```php
44+
#[Assert\NotBlank]
45+
public ?BelgiumPostCode $postcode = null;
46+
```
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
namespace SumoCoders\FrameworkCoreBundle\Form\Type;
4+
5+
use SumoCoders\FrameworkCoreBundle\Intl\BelgiumPostCodes;
6+
use SumoCoders\FrameworkCoreBundle\ValueObject\BelgiumPostCode;
7+
use Symfony\Component\Form\AbstractType;
8+
use Symfony\Component\Form\CallbackTransformer;
9+
use Symfony\Component\Form\ChoiceList\ChoiceList;
10+
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
11+
use Symfony\Component\Form\Exception\LogicException;
12+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
13+
use Symfony\Component\Form\FormBuilderInterface;
14+
use Symfony\Component\Intl\Intl;
15+
use Symfony\Component\OptionsResolver\Options;
16+
use Symfony\Component\OptionsResolver\OptionsResolver;
17+
18+
class BelgiumPostCodeType extends AbstractType
19+
{
20+
public function buildForm(FormBuilderInterface $builder, array $options): void
21+
{
22+
$builder->addModelTransformer(
23+
new CallbackTransformer(
24+
function (?BelgiumPostCode $object): ?string {
25+
return $object ? ($object->postcode . '|' . $object->municipality) : null;
26+
},
27+
function (?string $postcodeKey): ?BelgiumPostCode {
28+
if ($postcodeKey === null || $postcodeKey === '') {
29+
return null;
30+
}
31+
32+
[$postcode, $municipality] = explode('|', $postcodeKey, 2);
33+
return new BelgiumPostCode(
34+
$postcode,
35+
$municipality
36+
);
37+
}
38+
)
39+
);
40+
}
41+
42+
public function configureOptions(OptionsResolver $resolver): void
43+
{
44+
$resolver->setDefaults([
45+
'choice_loader' => function (Options $options) {
46+
if (!class_exists(Intl::class)) {
47+
throw new LogicException(sprintf('The "symfony/intl" component is required to use "%s". Try running "composer require symfony/intl".', static::class)); // phpcs:ignore Generic.Files.LineLength
48+
}
49+
50+
return ChoiceList::loader(
51+
$this,
52+
new IntlCallbackChoiceLoader(static fn() => array_flip(BelgiumPostCodes::getNames()))
53+
);
54+
},
55+
'choice_translation_domain' => false,
56+
'invalid_message' => 'Please select a valid postcode.',
57+
'placeholder' => '',
58+
'autocomplete' => true,
59+
'label' => 'Postcode and municipality',
60+
]);
61+
}
62+
63+
public function getParent(): ?string
64+
{
65+
return ChoiceType::class;
66+
}
67+
68+
public function getBlockPrefix(): string
69+
{
70+
return 'sumoBelgiumPostCode';
71+
}
72+
}

src/Intl/BelgiumPostCodes.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace SumoCoders\FrameworkCoreBundle\Intl;
4+
5+
use Symfony\Component\Intl\ResourceBundle;
6+
7+
class BelgiumPostCodes extends ResourceBundle
8+
{
9+
/**
10+
* @return array<string, string>
11+
*/
12+
public static function getNames(): array
13+
{
14+
return self::readEntry(['Names'], 'nl', false);
15+
}
16+
17+
protected static function getPath(): string
18+
{
19+
return __DIR__ . '/Resources/data/belgiumpostcodes';
20+
}
21+
}

0 commit comments

Comments
 (0)