diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 5d01cb3893..537ba84286 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +4.1.0 +----- + + * added `input=datetime_immutable` to DateType, TimeType, DateTimeType + 4.0.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php new file mode 100644 index 0000000000..b0737393e4 --- /dev/null +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformer.php @@ -0,0 +1,67 @@ + + * + * 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 DateTimeImmutable object and a DateTime object. + * + * @author Valentin Udaltsov + */ +final class DateTimeImmutableToDateTimeTransformer implements DataTransformerInterface +{ + /** + * Transforms a DateTimeImmutable into a DateTime object. + * + * @param \DateTimeImmutable|null $value A DateTimeImmutable object + * + * @return \DateTime|null A \DateTime object + * + * @throws TransformationFailedException If the given value is not a \DateTimeImmutable + */ + public function transform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTimeImmutable) { + throw new TransformationFailedException('Expected a \DateTimeImmutable.'); + } + + return \DateTime::createFromFormat(\DateTime::RFC3339, $value->format(\DateTime::RFC3339)); + } + + /** + * Transforms a DateTime object into a DateTimeImmutable object. + * + * @param \DateTime|null $value A DateTime object + * + * @return \DateTimeImmutable|null A DateTimeImmutable object + * + * @throws TransformationFailedException If the given value is not a \DateTime + */ + public function reverseTransform($value) + { + if (null === $value) { + return null; + } + + if (!$value instanceof \DateTime) { + throw new TransformationFailedException('Expected a \DateTime.'); + } + + return \DateTimeImmutable::createFromMutable($value); + } +} diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index d34d1c2100..06d085d30c 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormView; use Symfony\Component\Form\ReversedTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; @@ -165,7 +166,9 @@ class DateTimeType extends AbstractType ; } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone']) )); @@ -254,6 +257,7 @@ class DateTimeType extends AbstractType $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index bf51b15b6a..91aa8e3839 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -15,6 +15,7 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; @@ -123,7 +124,9 @@ class DateType extends AbstractType ; } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d') )); @@ -258,6 +261,7 @@ class DateType extends AbstractType $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index d1c04f73d9..df8973d932 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -18,6 +18,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\ReversedTransformer; use Symfony\Component\Form\Exception\InvalidConfigurationException; +use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeImmutableToDateTimeTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer; use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer; @@ -133,7 +134,9 @@ class TimeType extends AbstractType $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); } - if ('string' === $options['input']) { + if ('datetime_immutable' === $options['input']) { + $builder->addModelTransformer(new DateTimeImmutableToDateTimeTransformer()); + } elseif ('string' === $options['input']) { $builder->addModelTransformer(new ReversedTransformer( new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s') )); @@ -252,6 +255,7 @@ class TimeType extends AbstractType $resolver->setAllowedValues('input', array( 'datetime', + 'datetime_immutable', 'string', 'timestamp', 'array', diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php new file mode 100644 index 0000000000..244ef0f790 --- /dev/null +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeImmutableToDateTimeTransformerTest.php @@ -0,0 +1,76 @@ + + * + * 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\DateTimeImmutableToDateTimeTransformer; + +class DateTimeImmutableToDateTimeTransformerTest extends TestCase +{ + public function testTransform() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $input = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); + $expectedOutput = new \DateTime('2010-02-03 04:05:06 UTC'); + $actualOutput = $transformer->transform($input); + + $this->assertInstanceOf(\DateTime::class, $actualOutput); + $this->assertEquals($expectedOutput, $actualOutput); + } + + public function testTransformEmpty() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $this->assertNull($transformer->transform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage Expected a \DateTimeImmutable. + */ + public function testTransformFail() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + $transformer->transform(new \DateTime()); + } + + public function testReverseTransform() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $input = new \DateTime('2010-02-03 04:05:06 UTC'); + $expectedOutput = new \DateTimeImmutable('2010-02-03 04:05:06 UTC'); + $actualOutput = $transformer->reverseTransform($input); + + $this->assertInstanceOf(\DateTimeImmutable::class, $actualOutput); + $this->assertEquals($expectedOutput, $actualOutput); + } + + public function testReverseTransformEmpty() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + + $this->assertNull($transformer->reverseTransform(null)); + } + + /** + * @expectedException \Symfony\Component\Form\Exception\TransformationFailedException + * @expectedExceptionMessage Expected a \DateTime. + */ + public function testReverseTransformFail() + { + $transformer = new DateTimeImmutableToDateTimeTransformer(); + $transformer->reverseTransform(new \DateTimeImmutable()); + } +} diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php index a22da49ac5..0602ca5e13 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -52,6 +52,34 @@ class DateTimeTypeTest extends BaseTypeTest $this->assertEquals($dateTime, $form->getData()); } + public function testSubmitDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'date_widget' => 'choice', + 'years' => array(2010), + 'time_widget' => 'choice', + 'input' => 'datetime_immutable', + )); + + $form->submit(array( + 'date' => array( + 'day' => '2', + 'month' => '6', + 'year' => '2010', + ), + 'time' => array( + 'hour' => '3', + 'minute' => '4', + ), + )); + + $dateTime = new \DateTimeImmutable('2010-06-02 03:04:00 UTC'); + + $this->assertEquals($dateTime, $form->getData()); + } + public function testSubmitString() { $form = $this->factory->create(static::TESTED_TYPE, null, array( @@ -219,6 +247,26 @@ class DateTimeTypeTest extends BaseTypeTest $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); } + public function testSubmitDifferentTimezonesDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'America/New_York', + 'view_timezone' => 'Pacific/Tahiti', + 'widget' => 'single_text', + 'input' => 'datetime_immutable', + )); + + $outputTime = new \DateTimeImmutable('2010-06-02 03:04:00 Pacific/Tahiti'); + + $form->submit('2010-06-02T03:04:00-10:00'); + + $outputTime = $outputTime->setTimezone(new \DateTimeZone('America/New_York')); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals($outputTime, $form->getData()); + $this->assertEquals('2010-06-02T03:04:00-10:00', $form->getViewData()); + } + public function testSubmitStringSingleText() { $form = $this->factory->create(static::TESTED_TYPE, null, array( diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index 3a0289a4f3..c8d1b4614d 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -105,6 +105,28 @@ class DateTypeTest extends BaseTypeTest $this->assertEquals('02.06.2010', $form->getViewData()); } + public function testSubmitFromSingleTextDateTimeImmutable() + { + // we test against "de_DE", so we need the full implementation + IntlTestHelper::requireFullIntl($this, false); + + \Locale::setDefault('de_DE'); + + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'format' => \IntlDateFormatter::MEDIUM, + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'widget' => 'single_text', + 'input' => 'datetime_immutable', + )); + + $form->submit('2.6.2010'); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals(new \DateTimeImmutable('2010-06-02 UTC'), $form->getData()); + $this->assertEquals('02.06.2010', $form->getViewData()); + } + public function testSubmitFromSingleTextString() { // we test against "de_DE", so we need the full implementation diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 56b1d14774..c58cb44fa8 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -39,6 +39,28 @@ class TimeTypeTest extends BaseTypeTest $this->assertEquals($input, $form->getViewData()); } + public function testSubmitDateTimeImmutable() + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'datetime_immutable', + )); + + $input = array( + 'hour' => '3', + 'minute' => '4', + ); + + $form->submit($input); + + $dateTime = new \DateTimeImmutable('1970-01-01 03:04:00 UTC'); + + $this->assertInstanceOf(\DateTimeImmutable::class, $form->getData()); + $this->assertEquals($dateTime, $form->getData()); + $this->assertEquals($input, $form->getViewData()); + } + public function testSubmitString() { $form = $this->factory->create(static::TESTED_TYPE, null, array(