Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Form] Add intl/choice_translation_locale option to TimezoneType #31294

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions 55 src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\ChoiceList\Loader\IntlCallbackChoiceLoader;
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\Intl\Timezones;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

Expand All @@ -40,19 +42,41 @@ public function buildForm(FormBuilderInterface $builder, array $options)
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'intl' => false,
ro0NL marked this conversation as resolved.
Show resolved Hide resolved
'choice_loader' => function (Options $options) {
$regions = $options->offsetGet('regions', false);
$input = $options['input'];

if ($options['intl']) {
$choiceTranslationLocale = $options['choice_translation_locale'];

return new IntlCallbackChoiceLoader(function () use ($input, $choiceTranslationLocale) {
return self::getIntlTimezones($input, $choiceTranslationLocale);
});
}

$regions = $options->offsetGet('regions', false);

return new CallbackChoiceLoader(function () use ($regions, $input) {
return self::getTimezones($regions, $input);
return self::getPhpTimezones($regions, $input);
});
},
'choice_translation_domain' => false,
'choice_translation_locale' => null,
'input' => 'string',
'regions' => \DateTimeZone::ALL,
]);

$resolver->setAllowedTypes('intl', ['bool']);

$resolver->setAllowedTypes('choice_translation_locale', ['null', 'string']);
$resolver->setNormalizer('choice_translation_locale', function (Options $options, $value) {
if (null !== $value && !$options['intl']) {
throw new LogicException('The "choice_translation_locale" option can only be used if the "intl" option is set to true.');
}

return $value;
});

$resolver->setAllowedValues('input', ['string', 'datetimezone', 'intltimezone']);
$resolver->setNormalizer('input', function (Options $options, $value) {
if ('intltimezone' === $value && !class_exists(\IntlTimeZone::class)) {
Expand All @@ -64,6 +88,13 @@ public function configureOptions(OptionsResolver $resolver)

$resolver->setAllowedTypes('regions', 'int');
$resolver->setDeprecated('regions', 'The option "%name%" is deprecated since Symfony 4.2.');
$resolver->setNormalizer('regions', function (Options $options, $value) {
if ($options['intl'] && \DateTimeZone::ALL !== (\DateTimeZone::ALL & $value)) {
throw new LogicException('The "regions" option can only be used if the "intl" option is set to false.');
}

return $value;
});
}

/**
Expand All @@ -82,10 +113,7 @@ public function getBlockPrefix()
return 'timezone';
}

/**
* Returns a normalized array of timezone choices.
*/
private static function getTimezones(int $regions, string $input): array
private static function getPhpTimezones(int $regions, string $input): array
{
$timezones = [];

Expand All @@ -99,4 +127,19 @@ private static function getTimezones(int $regions, string $input): array

return $timezones;
}

private static function getIntlTimezones(string $input, string $locale = null): array
{
$timezones = array_flip(Timezones::getNames($locale));

if ('intltimezone' === $input) {
foreach ($timezones as $name => $timezone) {
if ('Etc/Unknown' === \IntlTimeZone::createTimeZone($timezone)->getID()) {
unset($timezones[$name]);
}
}
}

return $timezones;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;

use Symfony\Component\Form\ChoiceList\View\ChoiceView;
use Symfony\Component\Intl\Util\IntlTestHelper;

class TimezoneTypeTest extends BaseTypeTest
{
Expand Down Expand Up @@ -83,6 +84,17 @@ public function testFilterByRegions()
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Europe / Amsterdam'), $choices, '', false, false);
}

/**
* @group legacy
* @expectedDeprecation The option "regions" is deprecated since Symfony 4.2.
* @expectedException \Symfony\Component\Form\Exception\LogicException
* @expectedExceptionMessage The "regions" option can only be used if the "intl" option is set to false.
*/
public function testFilterByRegionsWithIntl()
{
$this->factory->create(static::TESTED_TYPE, null, ['regions' => \DateTimeZone::EUROPE, 'intl' => true]);
}

/**
* @requires extension intl
*/
Expand Down Expand Up @@ -116,4 +128,54 @@ public function testIntlTimeZoneInputWithBc()
$this->assertNull($form->getData());
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
}

/**
* @requires extension intl
*/
public function testIntlTimeZoneInputWithBcAndIntl()
{
$form = $this->factory->create(static::TESTED_TYPE, null, ['input' => 'intltimezone', 'intl' => true]);
$form->submit('Europe/Saratov');

$this->assertNull($form->getData());
$this->assertNotContains('Europe/Saratov', $form->getConfig()->getAttribute('choice_list')->getValues());
}

public function testTimezonesAreSelectableWithIntl()
{
IntlTestHelper::requireIntl($this, false);

$choices = $this->factory->create(static::TESTED_TYPE, null, ['intl' => true])
->createView()->vars['choices'];

$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Central European Time (Amsterdam)'), $choices, '', false, false);
$this->assertContains(new ChoiceView('Etc/UTC', 'Etc/UTC', 'Coordinated Universal Time'), $choices, '', false, false);
}

/**
* @requires extension intl
*/
public function testChoiceTranslationLocaleOptionWithIntl()
{
$choices = $this->factory
->create(static::TESTED_TYPE, null, [
'intl' => true,
'choice_translation_locale' => 'uk',
])
->createView()->vars['choices'];

$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'за центральноєвропейським часом (Амстердам)'), $choices, '', false, false);
$this->assertContains(new ChoiceView('Etc/UTC', 'Etc/UTC', 'за всесвітнім координованим часом'), $choices, '', false, false);
}

/**
* @expectedException \Symfony\Component\Form\Exception\LogicException
* @expectedExceptionMessage The "choice_translation_locale" option can only be used if the "intl" option is set to true.
*/
public function testChoiceTranslationLocaleOptionWithoutIntl()
{
$this->factory->create(static::TESTED_TYPE, null, [
'choice_translation_locale' => 'uk',
]);
}
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.