feature #31195 [Form] Add intltimezone input to TimezoneType (ro0NL)
This PR was squashed before being merged into the 4.3-dev branch (closes #31195).
Discussion
----------
[Form] Add intltimezone input to TimezoneType
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass? | yes <!-- please add some, will be required by reviewers -->
| Fixed tickets | #22302 (ref #28836)
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/issues/11463
cc @rvanlaak
Commits
-------
e169dfb968
[Form] Add intltimezone input to TimezoneType
This commit is contained in:
commit
73d303a1fe
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
|
||||
|
||||
use Symfony\Component\Form\DataTransformerInterface;
|
||||
use Symfony\Component\Form\Exception\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms between a timezone identifier string and a IntlTimeZone object.
|
||||
*
|
||||
* @author Roland Franssen <franssen.roland@gmail.com>
|
||||
*/
|
||||
class IntlTimeZoneToStringTransformer implements DataTransformerInterface
|
||||
{
|
||||
private $multiple;
|
||||
|
||||
public function __construct(bool $multiple = false)
|
||||
{
|
||||
$this->multiple = $multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($intlTimeZone)
|
||||
{
|
||||
if (null === $intlTimeZone) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($intlTimeZone)) {
|
||||
throw new TransformationFailedException('Expected an array of \IntlTimeZone objects.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'transform'], $intlTimeZone);
|
||||
}
|
||||
|
||||
if (!$intlTimeZone instanceof \IntlTimeZone) {
|
||||
throw new TransformationFailedException('Expected a \IntlTimeZone object.');
|
||||
}
|
||||
|
||||
return $intlTimeZone->getID();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reverseTransform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
if (!\is_array($value)) {
|
||||
throw new TransformationFailedException('Expected an array of timezone identifier strings.');
|
||||
}
|
||||
|
||||
return array_map([new self(), 'reverseTransform'], $value);
|
||||
}
|
||||
|
||||
if (!\is_string($value)) {
|
||||
throw new TransformationFailedException('Expected a timezone identifier string.');
|
||||
}
|
||||
|
||||
$intlTimeZone = \IntlTimeZone::createTimeZone($value);
|
||||
|
||||
if ('Etc/Unknown' === $intlTimeZone->getID()) {
|
||||
throw new TransformationFailedException(sprintf('Unknown timezone identifier "%s".', $value));
|
||||
}
|
||||
|
||||
return $intlTimeZone;
|
||||
}
|
||||
}
|
@ -13,7 +13,9 @@ namespace Symfony\Component\Form\Extension\Core\Type;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
|
||||
use Symfony\Component\Form\Exception\LogicException;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntlTimeZoneToStringTransformer;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
@ -27,6 +29,8 @@ class TimezoneType extends AbstractType
|
||||
{
|
||||
if ('datetimezone' === $options['input']) {
|
||||
$builder->addModelTransformer(new DateTimeZoneToStringTransformer($options['multiple']));
|
||||
} elseif ('intltimezone' === $options['input']) {
|
||||
$builder->addModelTransformer(new IntlTimeZoneToStringTransformer($options['multiple']));
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +42,10 @@ class TimezoneType extends AbstractType
|
||||
$resolver->setDefaults([
|
||||
'choice_loader' => function (Options $options) {
|
||||
$regions = $options->offsetGet('regions', false);
|
||||
$input = $options['input'];
|
||||
|
||||
return new CallbackChoiceLoader(function () use ($regions) {
|
||||
return self::getTimezones($regions);
|
||||
return new CallbackChoiceLoader(function () use ($regions, $input) {
|
||||
return self::getTimezones($regions, $input);
|
||||
});
|
||||
},
|
||||
'choice_translation_domain' => false,
|
||||
@ -48,7 +53,14 @@ class TimezoneType extends AbstractType
|
||||
'regions' => \DateTimeZone::ALL,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedValues('input', ['string', 'datetimezone']);
|
||||
$resolver->setAllowedValues('input', ['string', 'datetimezone', 'intltimezone']);
|
||||
$resolver->setNormalizer('input', function (Options $options, $value) {
|
||||
if ('intltimezone' === $value && !class_exists(\IntlTimeZone::class)) {
|
||||
throw new LogicException('Cannot use "intltimezone" input because the PHP intl extension is not available.');
|
||||
}
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
$resolver->setAllowedTypes('regions', 'int');
|
||||
$resolver->setDeprecated('regions', 'The option "%name%" is deprecated since Symfony 4.2.');
|
||||
@ -73,11 +85,15 @@ class TimezoneType extends AbstractType
|
||||
/**
|
||||
* Returns a normalized array of timezone choices.
|
||||
*/
|
||||
private static function getTimezones(int $regions): array
|
||||
private static function getTimezones(int $regions, string $input): array
|
||||
{
|
||||
$timezones = [];
|
||||
|
||||
foreach (\DateTimeZone::listIdentifiers($regions) as $timezone) {
|
||||
if ('intltimezone' === $input && 'Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parts = explode('/', $timezone);
|
||||
|
||||
if (\count($parts) > 2) {
|
||||
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Form\Extension\Core\DataTransformer\IntlTimeZoneToStringTransformer;
|
||||
|
||||
/**
|
||||
* @requires extension intl
|
||||
*/
|
||||
class IntlTimeZoneToStringTransformerTest extends TestCase
|
||||
{
|
||||
public function testSingle()
|
||||
{
|
||||
$transformer = new IntlTimeZoneToStringTransformer();
|
||||
|
||||
$this->assertNull($transformer->transform(null));
|
||||
$this->assertNull($transformer->reverseTransform(null));
|
||||
|
||||
$this->assertSame('Europe/Amsterdam', $transformer->transform(\IntlTimeZone::createTimeZone('Europe/Amsterdam')));
|
||||
$this->assertEquals(\IntlTimeZone::createTimeZone('Europe/Amsterdam'), $transformer->reverseTransform('Europe/Amsterdam'));
|
||||
}
|
||||
|
||||
public function testMultiple()
|
||||
{
|
||||
$transformer = new IntlTimeZoneToStringTransformer(true);
|
||||
|
||||
$this->assertNull($transformer->transform(null));
|
||||
$this->assertNull($transformer->reverseTransform(null));
|
||||
|
||||
$this->assertSame(['Europe/Amsterdam'], $transformer->transform([\IntlTimeZone::createTimeZone('Europe/Amsterdam')]));
|
||||
$this->assertEquals([\IntlTimeZone::createTimeZone('Europe/Amsterdam')], $transformer->reverseTransform(['Europe/Amsterdam']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||
*/
|
||||
public function testInvalidTimezone()
|
||||
{
|
||||
(new IntlTimeZoneToStringTransformer())->transform(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
|
||||
*/
|
||||
public function testUnknownTimezone()
|
||||
{
|
||||
(new IntlTimeZoneToStringTransformer(true))->reverseTransform(['Foo/Bar']);
|
||||
}
|
||||
}
|
@ -65,6 +65,15 @@ class TimezoneTypeTest extends BaseTypeTest
|
||||
$this->assertEquals([new \DateTimeZone('Europe/Amsterdam'), new \DateTimeZone('Europe/Paris')], $form->getData());
|
||||
}
|
||||
|
||||
public function testDateTimeZoneInputWithBc()
|
||||
{
|
||||
$form = $this->factory->create(static::TESTED_TYPE, null, ['input' => 'datetimezone']);
|
||||
$form->submit('Europe/Saratov');
|
||||
|
||||
$this->assertEquals(new \DateTimeZone('Europe/Saratov'), $form->getData());
|
||||
$this->assertContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation The option "regions" is deprecated since Symfony 4.2.
|
||||
@ -76,4 +85,38 @@ class TimezoneTypeTest extends BaseTypeTest
|
||||
|
||||
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Amsterdam'), $choices, '', false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension intl
|
||||
*/
|
||||
public function testIntlTimeZoneInput()
|
||||
{
|
||||
$form = $this->factory->create(static::TESTED_TYPE, \IntlTimeZone::createTimeZone('America/New_York'), ['input' => 'intltimezone']);
|
||||
|
||||
$this->assertSame('America/New_York', $form->createView()->vars['value']);
|
||||
|
||||
$form->submit('Europe/Amsterdam');
|
||||
|
||||
$this->assertEquals(\IntlTimeZone::createTimeZone('Europe/Amsterdam'), $form->getData());
|
||||
|
||||
$form = $this->factory->create(static::TESTED_TYPE, [\IntlTimeZone::createTimeZone('America/New_York')], ['input' => 'intltimezone', 'multiple' => true]);
|
||||
|
||||
$this->assertSame(['America/New_York'], $form->createView()->vars['value']);
|
||||
|
||||
$form->submit(['Europe/Amsterdam', 'Europe/Paris']);
|
||||
|
||||
$this->assertEquals([\IntlTimeZone::createTimeZone('Europe/Amsterdam'), \IntlTimeZone::createTimeZone('Europe/Paris')], $form->getData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires extension intl
|
||||
*/
|
||||
public function testIntlTimeZoneInputWithBc()
|
||||
{
|
||||
$form = $this->factory->create(static::TESTED_TYPE, null, ['input' => 'intltimezone']);
|
||||
$form->submit('Europe/Saratov');
|
||||
|
||||
$this->assertNull($form->getData());
|
||||
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user