[Form] Add input + regions options to TimezoneType

This commit is contained in:
Roland Franssen 2017-07-24 17:43:04 +02:00
parent 40a3466268
commit 183307b741
7 changed files with 272 additions and 5 deletions

View File

@ -74,6 +74,38 @@ Finder
deprecated and will be removed in 4.0 as it used to fix a bug which existed
before version 5.5.23/5.6.7.
Form
----
* Deprecated `ChoiceLoaderInterface` implementation in `TimezoneType`. Use the "choice_loader" option instead.
Before:
```php
class MyTimezoneType extends TimezoneType
{
public function loadChoices()
{
// override the method
}
}
```
After:
```php
class MyTimezoneType extends AbstractType
{
public function. getParent()
{
return TimezoneType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('choice_loader', ...); // override the option instead
}
}
```
FrameworkBundle
---------------

View File

@ -282,6 +282,35 @@ Form
));
```
* Removed `ChoiceLoaderInterface` implementation in `TimezoneType`. Use the "choice_loader" option instead.
Before:
```php
class MyTimezoneType extends TimezoneType
{
public function loadChoices()
{
// override the method
}
}
```
After:
```php
class MyTimezoneType extends AbstractType
{
public function. getParent()
{
return TimezoneType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('choice_loader', ...); // override the option instead
}
}
```
FrameworkBundle
---------------

View File

@ -5,6 +5,8 @@ CHANGELOG
-----
* added `DebugCommand`
* deprecated `ChoiceLoaderInterface` implementation in `TimezoneType`
* added options "input" and "regions" to `TimezoneType`
3.3.0
-----

View File

@ -0,0 +1,82 @@
<?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 DateTimeZone object.
*
* @author Roland Franssen <franssen.roland@gmai.com>
*/
class DateTimeZoneToStringTransformer implements DataTransformerInterface
{
private $multiple;
public function __construct($multiple = false)
{
$this->multiple = $multiple;
}
/**
* {@inheritdoc}
*/
public function transform($dateTimeZone)
{
if (null === $dateTimeZone) {
return;
}
if ($this->multiple) {
if (!is_array($dateTimeZone)) {
throw new TransformationFailedException('Expected an array.');
}
return array_map(array(new self(), 'transform'), $dateTimeZone);
}
if (!$dateTimeZone instanceof \DateTimeZone) {
throw new TransformationFailedException('Expected a \DateTimeZone.');
}
return $dateTimeZone->getName();
}
/**
* {@inheritdoc}
*/
public function reverseTransform($value)
{
if (null === $value) {
return;
}
if ($this->multiple) {
if (!is_array($value)) {
throw new TransformationFailedException('Expected an array.');
}
return array_map(array(new self(), 'reverseTransform'), $value);
}
if (!is_string($value)) {
throw new TransformationFailedException('Expected a string.');
}
try {
return new \DateTimeZone($value);
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
}
}

View File

@ -13,7 +13,10 @@ namespace Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -25,9 +28,21 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
* The choices are generated from the ICU function \DateTimeZone::listIdentifiers().
*
* @var ArrayChoiceList
*
* @deprecated since version 3.4, to be removed in 4.0
*/
private $choiceList;
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ('datetimezone' === $options['input']) {
$builder->addModelTransformer(new DateTimeZoneToStringTransformer($options['multiple']));
}
}
/**
* {@inheritdoc}
*/
@ -41,10 +56,20 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
return null;
}
return $this;
$regions = $options['regions'];
return new CallbackChoiceLoader(function () use ($regions) {
return self::getTimezones($regions);
});
},
'choice_translation_domain' => false,
'input' => 'string',
'regions' => \DateTimeZone::ALL,
));
$resolver->setAllowedValues('input', array('string', 'datetimezone'));
$resolver->setAllowedTypes('regions', 'int');
}
/**
@ -65,21 +90,29 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
/**
* {@inheritdoc}
*
* @deprecated since version 3.4, to be removed in 4.0
*/
public function loadChoiceList($value = null)
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
if (null !== $this->choiceList) {
return $this->choiceList;
}
return $this->choiceList = new ArrayChoiceList(self::getTimezones(), $value);
return $this->choiceList = new ArrayChoiceList(self::getTimezones(\DateTimeZone::ALL), $value);
}
/**
* {@inheritdoc}
*
* @deprecated since version 3.4, to be removed in 4.0
*/
public function loadChoicesForValues(array $values, $value = null)
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
// Optimize
$values = array_filter($values);
if (empty($values)) {
@ -96,9 +129,13 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
/**
* {@inheritdoc}
*
* @deprecated since version 3.4, to be removed in 4.0
*/
public function loadValuesForChoices(array $choices, $value = null)
{
@trigger_error(sprintf('Method "%s" is deprecated since version 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED);
// Optimize
$choices = array_filter($choices);
if (empty($choices)) {
@ -116,13 +153,15 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
/**
* Returns a normalized array of timezone choices.
*
* @param int $regions
*
* @return array The timezone choices
*/
private static function getTimezones()
private static function getTimezones($regions)
{
$timezones = array();
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
foreach (\DateTimeZone::listIdentifiers($regions) as $timezone) {
$parts = explode('/', $timezone);
if (count($parts) > 2) {
@ -139,6 +178,6 @@ class TimezoneType extends AbstractType implements ChoiceLoaderInterface
$timezones[$region][str_replace('_', ' ', $name)] = $timezone;
}
return $timezones;
return 1 === count($timezones) ? reset($timezones) : $timezones;
}
}

View File

@ -0,0 +1,56 @@
<?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 Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeZoneToStringTransformer;
use PHPUnit\Framework\TestCase;
class DateTimeZoneToStringTransformerTest extends TestCase
{
public function testSingle()
{
$transformer = new DateTimeZoneToStringTransformer();
$this->assertNull($transformer->transform(null));
$this->assertNull($transformer->reverseTransform(null));
$this->assertSame('Europe/Amsterdam', $transformer->transform(new \DateTimeZone('Europe/Amsterdam')));
$this->assertEquals(new \DateTimeZone('Europe/Amsterdam'), $transformer->reverseTransform('Europe/Amsterdam'));
}
public function testMultiple()
{
$transformer = new DateTimeZoneToStringTransformer(true);
$this->assertNull($transformer->transform(null));
$this->assertNull($transformer->reverseTransform(null));
$this->assertSame(array('Europe/Amsterdam'), $transformer->transform(array(new \DateTimeZone('Europe/Amsterdam'))));
$this->assertEquals(array(new \DateTimeZone('Europe/Amsterdam')), $transformer->reverseTransform(array('Europe/Amsterdam')));
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testInvalidTimezone()
{
(new DateTimeZoneToStringTransformer())->transform(1);
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testUnknownTimezone()
{
(new DateTimeZoneToStringTransformer(true))->reverseTransform(array('Foo/Bar'));
}
}

View File

@ -33,4 +33,31 @@ class TimezoneTypeTest extends BaseTypeTest
{
parent::testSubmitNull($expected, $norm, '');
}
public function testDateTimeZoneInput()
{
$form = $this->factory->create(static::TESTED_TYPE, new \DateTimeZone('America/New_York'), array('input' => 'datetimezone'));
$this->assertSame('America/New_York', $form->createView()->vars['value']);
$form->submit('Europe/Amsterdam');
$this->assertEquals(new \DateTimeZone('Europe/Amsterdam'), $form->getData());
$form = $this->factory->create(static::TESTED_TYPE, array(new \DateTimeZone('America/New_York')), array('input' => 'datetimezone', 'multiple' => true));
$this->assertSame(array('America/New_York'), $form->createView()->vars['value']);
$form->submit(array('Europe/Amsterdam', 'Europe/Paris'));
$this->assertEquals(array(new \DateTimeZone('Europe/Amsterdam'), new \DateTimeZone('Europe/Paris')), $form->getData());
}
public function testFilterByRegions()
{
$choices = $this->factory->create(static::TESTED_TYPE, null, array('regions' => \DateTimeZone::EUROPE))
->createView()->vars['choices'];
$this->assertContains(new ChoiceView('Europe/Amsterdam', 'Europe/Amsterdam', 'Amsterdam'), $choices, '', false, false);
}
}