10000 feature #23648 [Form] Add input + regions options to TimezoneType (r… · symfony/symfony@9208244 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9208244

Browse files
committed
feature #23648 [Form] Add input + regions options to TimezoneType (ro0NL)
This PR was merged into the 3.4 branch. Discussion ---------- [Form] Add input + regions options to TimezoneType | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #... <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | symfony/symfony-docs#8223 I want to use `\DateTimeZone` as a model format, with only european timezones in the form available. Hence the new options. Commits ------- 183307b [Form] Add input + regions options to TimezoneType
2 parents 40a3466 + 183307b commit 9208244

File tree

7 files changed

+272
-5
lines changed

7 files changed

+272
-5
lines changed

UPGRADE-3.4.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,38 @@ Finder
7474
deprecated and will be removed in 4.0 as it used to fix a bug which existed
7575
before version 5.5.23/5.6.7.
7676

77+
Form
78+
----
79+
80+
* Deprecated `ChoiceLoaderInterface` implementation in `TimezoneType`. Use the "choice_loader" option instead.
81+
82+
Before:
83+
```php
84+
class MyTimezoneType extends TimezoneType
85+
{
86+
public function loadChoices()
87+
{
88+
// override the method
89+
}
90+
}
91+
```
92+
93+
After:
94+
```php
95+
class MyTimezoneType extends AbstractType
96+
{
97+
public function. getParent()
98+
{
99+
return TimezoneType::class;
100+
}
101+
102+
public function configureOptions(OptionsResolver $resolver)
103+
{
104+
$resolver->setDefault('choice_loader', ...); // override the option instead
105+
}
106+
}
107+
```
108+
77109
FrameworkBundle
78110
---------------
79111

UPGRADE-4.0.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,35 @@ Form
282282
));
283283
```
284284

285+
* Removed `ChoiceLoaderInterface` implementation in `TimezoneType`. Use the "choice_loader" option instead.
286+
287+
Before:
288+
```php
289+
class MyTimezoneType extends TimezoneType
290+
{
291+
public function loadChoices()
292+
{
293+
// override the method
294+
}
295+
}
296+
```
297+
298+
After:
299+
```php
300+
class MyTimezoneType extends AbstractType
301+
{
302+
public function. getParent()
303+
{
304+
return TimezoneType::class;
305+
}
306+
307+
public function configureOptions(OptionsResolver $resolver)
308+
{
309+
$resolver->setDefault('choice_loader', ...); // override the option instead
310+
}
311+
}
312+
```
313+
285314
FrameworkBundle
286315
---------------
287316

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55
-----
66

77
* added `DebugCommand`
8+
* deprecated `ChoiceLoaderInterface` implementation in `TimezoneType`
9+
* added options "input" and "regions" to `TimezoneType`
810

911
3.3.0
1012
-----
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
13+
14+
use Symfony\Component\Form\DataTransformerInterface;
15+
use Symfony\Component\Form\Exception\TransformationFailedException;
16+
17+
/**
18+
* Transforms between a timezone identifier string and a DateTimeZone object.
19+
*
20+
* @author Roland Franssen <franssen.roland@gmai.com>
21+
*/
22+
class DateTimeZoneToStringTransformer implements DataTransformerInterface
23+
{
24+
private $multiple;
25+
26+
public function __construct($multiple = false)
27+
{
28+
$this->multiple = $multiple;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function transform($dateTimeZone)
35+
{
36+
if (null === $dateTimeZone) {
37+
return;
38+
}
39+
40+
if ($this->multiple) {
41+
if (!is_array($dateTimeZone)) {
42+
throw new TransformationFailedException('Expected an array.');
43+
}
44+
45+
return array_map(array(new self(), 'transform'), $dateTimeZone);
46+
}
47+
48+
if (!$dateTimeZone instanceof \DateTimeZone) {
49+
throw new TransformationFailedException('Expected a \DateTimeZone.');
50+
}
51+
52+
return $dateTimeZone->getName();
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function reverseTransform($value)
59+
{
60+
if (null === $value) {
61+
return;
62+
}
63+
64+
if ($this->multiple) {
65+
if (!is_array($value)) {
66+
throw new TransformationFailedException('Expected an array.');
67+
}
68+
69+
return array_map(array(new self(), 'reverseTransform'), $value);
70+
}
71+
72+
if (!is_string($value)) {
73+
throw new TransformationFailedException('Expected a string.');
74+
}
75+
76+
try {
77+
return new \DateTimeZone($value);
78+
} catch (\Exception $e) {
79+
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
80+
}
81+
}
82+
}

src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
use Symfony\Component\Form\AbstractType;
1515
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
16+
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
1617
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
18+
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
19+
use Symfony\Component\Form\FormBuilderInterface;
1720
use Symfony\Component\OptionsResolver\Options;
1821
use Symfony\Component\OptionsResolver\OptionsResolver;
1922

@@ -25,9 +28,21 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
2528
* The choices are generated from the ICU function \DateTimeZone::listIdentifiers().
2629
*
2730
* @var ArrayChoiceList
31+
*
32+
* @deprecated since version 3.4, to be removed in 4.0
2833
*/
2934
private $choiceList;
3035

36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function buildForm(FormBuilderInterface $builder, array $options)
40+
{
41+
if ('datetimezone' === $options['input']) {
42+
$builder->addModelTransformer(new DateTimeZoneToStringTransformer($options['multiple']));
43+
}
44+
}
45+
3146
/**
3247
* {@inheritdoc}
3348
*/
@@ -41,10 +56,20 @@ public function configureOptions(OptionsResolver $resolver)
4156
return null;
4257
}
4358

44-
return $this;
59+
$regions = $options['regions'];
60+
61+
return new CallbackChoiceLoader(function () use ($regions) {
62+
return self::getTimezones($regions);
63+
});
4564
},
4665
'choice_translation_domain' => false,
66+
'input' => 'string',
67+
'regions' => \DateTimeZone::ALL,
4768
));
69+
70+
$resolver->setAllowedValues('input', array('string', 'datetimezone'));
71+
72+
$resolver->setAllowedTypes('regions', 'int');
4873
}
4974

5075
/**
@@ -65,21 +90,29 @@ public function getBlockPrefix()
6590

6691
/**
6792
* {@inheritdoc}
93+
*
94+
* @deprecated since version 3.4, to be removed in 4.0
6895
*/
6996
public function loadChoiceList($value = null)
7097
{
98+
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
99+
71100
if (null !== $this->choiceList) {
72101
return $this->choiceList;
73102
}
74103

75-
return $this->choiceList = new ArrayChoiceList(self::getTimezones(), $value);
104+
return $this->choiceList = new ArrayChoiceList(self::getTimezones(\DateTimeZone::ALL), $value);
76105
}
77106

78107
/**
79108
* {@inheritdoc}
109+
*
110+
* @deprecated since version 3.4, to be removed in 4.0
80111
*/
81112
public function loadChoicesForValues(array $values, $value = null)
82113
{
114+
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
115+
83116
// Optimize
84117
$values = array_filter($values);
85118
if (empty($values)) {
@@ -96,9 +129,13 @@ public function loadChoicesForValues(array $values, $value = null)
96129

97130
/**
98131
* {@inheritdoc}
132+
*
133+
* @deprecated since version 3.4, to be removed in 4.0
99134
*/
100135
public function loadValuesForChoices(array $choices, $value = null)
101136
{
137+
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
138+
102139
// Optimize
103140
$choices = array_filter($choices);
104141
if (empty($choices)) {
@@ -116,13 +153,15 @@ public function loadValuesForChoices(array $choices, $value = null)
116153
/**
117154
* Returns a normalized array of timezone choices.
118155
*
156+
* @param int $regions
157+
*
119158
* @return array The timezone choices
120159
*/
121-
private static function getTimezones()
160+
private static function getTimezones($regions)
122161
{
123162
$timezones = array();
124163

125-
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
164+
foreach (\DateTimeZone::listIdentifiers($regions) as $timezone) {
126165
$parts = explode('/', $timezone);
127166

128167
if (count($parts) > 2) {
@@ -139,6 +178,6 @@ private static function getTimezones()
139178
$timezones[$region][str_replace('_', ' ', $name)] = $timezone;
140179
}
141180

142-
return $timezones;
181+
return 1 === count($timezones) ? reset($timezones) : $timezones;
143182
}
144183
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
13+
14+
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
15+
use PHPUnit\Framework\TestCase;
16+
17+
class DateTimeZoneToStringTransformerTest extends TestCase
18+
{
19+
public function testSingle()
20+
{
21+
$transformer = new DateTimeZoneToStringTransformer();
22+
23+
$this->assertNull($transformer->transform(null));
24+
$this->assertNull($transformer->reverseTransform(null));
25+
26+
$this->assertSame('Europe/Amsterdam', $transformer->transform(new \DateTimeZone('Europe/Amsterdam')));
27+
$this->assertEquals(new \DateTimeZone('Europe/Amsterdam'), $transformer->reverseTransform('Europe/Amsterdam'));
28+
}
29+
30+
public function testMultiple()
31+
{
32+
$transformer = new DateTimeZoneToStringTransformer(true);
33+
34+
$this->assertNull($transformer->transform(null));
35+
$this->assertNull($transformer->reverseTransform(null));
36+
37+
$this->assertSame(array('Europe/Amsterdam'), $transformer->transform(array(new \DateTimeZone('Europe/Amsterdam'))));
38+
$this->assertEquals(array(new \DateTimeZone('Europe/Amsterdam')), $transformer->reverseTransform(array('Europe/Amsterdam')));
39+
}
40+
41+
/**
42+
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
43+
*/
44+
public function testInvalidTimezone()
45+
{
46+
(new DateTimeZoneToStringTransformer())->transform(1);
47+
}
48+
49+
/**
50+
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
51+
*/
52+
public function testUnknownTimezone()
53+
{
54+
(new DateTimeZoneToStringTransformer(true))->reverseTransform(array('Foo/Bar'));
55+
}
56+
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/TimezoneTypeTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,31 @@ public function testSubmitNull($expected = null, $norm = null, $view = null)
3333
{
3434
parent::testSubmitNull($expected, $norm, '');
3535
}
36+
37+
public function testDateTimeZoneInput()
38+
{
39+
$form = $this->factory->create(static::TESTED_TYPE, new \DateTimeZone('America/New_York'), array('input' => 'datetimezone'));
40+
41+
$this->assertSame('America/New_York', $form->createView()->vars['value']);
42+
43+
$form->submit('Europe/Amsterdam');
44+
45+
$this->assertEquals(new \DateTimeZone('Europe/Amsterdam'), $form->getData());
46+
47+
$form = $this->factory->create(static::TESTED_TYPE, array(new \DateTimeZone('America/New_York')), array('input' => 'datetimezone', 'multiple' => true));
48+
49+
$this->assertSame(array('America/New_York'), $form->createView()->vars['value']);
50+
51+
$form->submit(array('Europe/Amsterdam', 'Europe/Paris'));
52+
53+
$this->assertEquals(array(new \DateTimeZone('Europe/Amsterdam'), new \DateTimeZone('Europe/Paris')), $form->getData());
54+
}
55+
56+
public function testFilterByRegions()
57+
{
58+
$choices = $this->factory->create(static::TESTED_TYPE, null, array('regions' => \DateTimeZone::EUROPE))
59+
->createView()->vars['choices'];
60+
61+
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Amsterdam'), $choices, '', false, false);
62+
}
3663
}

0 commit comments

Comments
 (0)
0