Skip to content

Commit f1dd184

Browse files
author
Erik van Velzen
committed
Initial commit
0 parents  commit f1dd184

File tree

6 files changed

+266
-0
lines changed

6 files changed

+266
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor
2+
composer.lock

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# ZF3 Country dropdown
2+
3+
Country dropdown for [zend-form](https://docs.zendframework.com/zend-form/) (Zend Framework 3).
4+
5+
Works with 2-letter ISO 3166 codes
6+
7+
Options are displayed in the current locale.
8+
9+
## Installation
10+
11+
```
12+
$ composer require polderknowledge/country-dropdown
13+
```
14+
15+
## Usage
16+
17+
in Form::init():
18+
19+
```php
20+
$this->add([
21+
'type' => CountrySelect::class,
22+
'name' => 'country',
23+
'options' => [
24+
// optional countries to display first in the dropdown above a separator
25+
'top_country_codes' => ['NL', 'ES'],
26+
],
27+
]);
28+
```
29+
30+
Rendering can be done in the same way as a normal select element.

composer.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "polderknowledge/country-dropdown",
3+
"description": "Country select box for Zend Framework 3",
4+
"type": "library",
5+
"autoload": {
6+
"psr-4": {
7+
"PolderKnowledge\\CountryDropdown\\": "src/"
8+
}
9+
},
10+
"require": {
11+
"php": ">= 7.0",
12+
"league/iso3166": "^2.1",
13+
"zendframework/zend-form": "^2.12",
14+
"lstrojny/functional-php": "^1.8"
15+
},
16+
"license": "MIT",
17+
"authors": [
18+
{
19+
"name": "Erik van Velzen",
20+
"email": "erik@polderknowledge.nl"
21+
}
22+
],
23+
"require-dev": {
24+
"phpunit/phpunit": "^7"
25+
}
26+
}

phpunit.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="./vendor/autoload.php">
3+
<testsuites>
4+
<testsuite name="PolderKnowledge Country Dropdown Module">
5+
<directory>./test</directory>
6+
</testsuite>
7+
</testsuites>
8+
</phpunit>

src/CountrySelect.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace PolderKnowledge\CountryDropdown;
4+
5+
use function Functional\map;
6+
use function Functional\pluck;
7+
use function Functional\sort;
8+
use League\ISO3166\ISO3166;
9+
use Zend\Form\Element\Select;
10+
11+
class CountrySelect extends Select
12+
{
13+
/**
14+
* The countries which should be on top so the end-user can find them easily
15+
*
16+
* @var string[]
17+
*/
18+
protected $topCountryCodes = [];
19+
20+
public function setTopCountryCodes(array $topCountryCodes)
21+
{
22+
$this->topCountryCodes = $topCountryCodes;
23+
}
24+
25+
public function getTopCountryCodes(): array
26+
{
27+
return $this->topCountryCodes;
28+
}
29+
30+
/**
31+
* Usually called by Zend\Form\Factory
32+
*/
33+
public function setOptions($options)
34+
{
35+
parent::setOptions($options);
36+
37+
if (isset($options['top_country_codes'])) {
38+
$this->setTopCountryCodes($options['top_country_codes']);
39+
}
40+
}
41+
42+
/**
43+
* Usually called by the FormElementManager
44+
* After all the options have been set
45+
* and just before handing the element to the user's form or fieldset
46+
*/
47+
public function init()
48+
{
49+
parent::init();
50+
51+
$this->valueOptions = self::generateValueOptions($this->getTopCountryCodes());
52+
}
53+
54+
public static function generateValueOptions(array $topCountryCodes): array
55+
{
56+
$bottomCountryCodes = array_diff(self::generateAllCountryCodes(), $topCountryCodes);
57+
$bottomOptions = self::countryCodesToOptions($bottomCountryCodes);
58+
$topOptions = self::countryCodesToOptions($topCountryCodes);
59+
60+
if (empty($topOptions)) {
61+
return $bottomOptions;
62+
}
63+
64+
$separatorOption = [
65+
'label' => '──────────',
66+
'disabled' => true,
67+
'value' => null,
68+
];
69+
70+
return array_merge(
71+
$topOptions,
72+
[$separatorOption],
73+
$bottomOptions
74+
);
75+
}
76+
77+
/**
78+
* @return string[]
79+
*/
80+
public static function generateAllCountryCodes(): array
81+
{
82+
return pluck(new ISO3166(), ISO3166::KEY_ALPHA2);
83+
}
84+
85+
public static function countryCodesToOptions(array $countryCodes): array
86+
{
87+
$options = map($countryCodes, [__CLASS__, 'countryCodeToOption']);
88+
89+
return sort($options, function (array $left, array $right) {
90+
return $left['label'] <=> $right['label'];
91+
});
92+
}
93+
94+
public static function countryCodeToOption(string $code): array
95+
{
96+
return [
97+
'value' => $code,
98+
'label' => \Locale::getDisplayRegion("-$code"),
99+
];
100+
}
101+
}

test/CountrySelectTest.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace PolderKnowledge\CountryDropdown\Test;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use PolderKnowledge\CountryDropdown\CountrySelect;
7+
use Zend\Form\Factory;
8+
use Zend\Form\FormElementManager\FormElementManagerV3Polyfill;
9+
use Zend\Form\View\Helper\FormSelect;
10+
use Zend\ServiceManager\ServiceManager;
11+
12+
class CountrySelectTest extends TestCase
13+
{
14+
protected function setUp()
15+
{
16+
\Locale::setDefault('de-DE');
17+
}
18+
19+
public function testCountryCodeToOption()
20+
{
21+
$option = CountrySelect::countryCodeToOption('NZ');
22+
23+
$this->assertEquals([
24+
'value' => 'NZ',
25+
'label' => 'Neuseeland'
26+
], $option);
27+
}
28+
29+
public function testCountriesAreSortedAlphabeticallyByLabel()
30+
{
31+
$options = CountrySelect::countryCodesToOptions(['CD', 'DK', 'AR']);
32+
33+
$this->assertEquals([
34+
[
35+
'value' => 'AR',
36+
'label' => 'Argentinien',
37+
], [
38+
'value' => 'DK',
39+
'label' => 'Dänemark',
40+
], [
41+
'value' => 'CD',
42+
'label' => 'Kongo-Kinshasa',
43+
],
44+
], $options);
45+
}
46+
47+
public function testSeparatorIsInserted()
48+
{
49+
$options = CountrySelect::generateValueOptions(['DE']);
50+
51+
$top3options = array_slice($options, 0, 3);
52+
53+
$this->assertEquals([
54+
[
55+
'value' => 'DE',
56+
'label' => 'Deutschland',
57+
], [
58+
'value' => null,
59+
'label' => '──────────',
60+
'disabled' => true,
61+
], [
62+
'value' => 'AF',
63+
'label' => 'Afghanistan',
64+
],
65+
], $top3options);
66+
}
67+
68+
public function testCreateElementViaFactory()
69+
{
70+
$factory = new Factory();
71+
72+
/** @var CountrySelect $countrySelect */
73+
$countrySelect = $factory->createElement([
74+
'type' => CountrySelect::class,
75+
'options' => [
76+
'top_country_codes' => ['DE']
77+
]
78+
]);
79+
80+
$this->assertInstanceOf(CountrySelect::class, $countrySelect);
81+
$this->assertEquals(['DE'], $countrySelect->getTopCountryCodes());
82+
}
83+
84+
public function testCreateElementViaManager()
85+
{
86+
$formElementManager = new FormElementManagerV3Polyfill(new ServiceManager());
87+
88+
/** @var CountrySelect $countrySelect */
89+
$countrySelect = $formElementManager->get(CountrySelect::class, [
90+
'top_country_codes' => ['DE']
91+
]);
92+
93+
$this->assertInstanceOf(CountrySelect::class, $countrySelect);
94+
$this->assertEquals(['DE'], $countrySelect->getTopCountryCodes());
95+
96+
// test if ->init() has been called
97+
$this->assertTrue(count($countrySelect->getValueOptions()) > 100);
98+
}
99+
}

0 commit comments

Comments
 (0)