feature #25582 [Form] Support \DateTimeImmutable (vudaltsov)
This PR was squashed before being merged into the 4.1-dev branch (closes #25582).
Discussion
----------
[Form] Support \DateTimeImmutable
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #9508
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/8920
This PR implements `input=datetime_immutable`. Replaces #25273.
Commits
-------
034f8b2f23
[Form] Support \DateTimeImmutable
This commit is contained in:
commit
ec89ac887d
@ -1,6 +1,11 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
4.1.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added `input=datetime_immutable` to DateType, TimeType, DateTimeType
|
||||||
|
|
||||||
4.0.0
|
4.0.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
<?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 DateTimeImmutable object and a DateTime object.
|
||||||
|
*
|
||||||
|
* @author Valentin Udaltsov <udaltsov.valentin@gmail.com>
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
|||||||
use Symfony\Component\Form\FormView;
|
use Symfony\Component\Form\FormView;
|
||||||
use Symfony\Component\Form\ReversedTransformer;
|
use Symfony\Component\Form\ReversedTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
|
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\DateTimeToArrayTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
|
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(
|
$builder->addModelTransformer(new ReversedTransformer(
|
||||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'])
|
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'])
|
||||||
));
|
));
|
||||||
@ -254,6 +257,7 @@ class DateTimeType extends AbstractType
|
|||||||
|
|
||||||
$resolver->setAllowedValues('input', array(
|
$resolver->setAllowedValues('input', array(
|
||||||
'datetime',
|
'datetime',
|
||||||
|
'datetime_immutable',
|
||||||
'string',
|
'string',
|
||||||
'timestamp',
|
'timestamp',
|
||||||
'array',
|
'array',
|
||||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormView;
|
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\DateTimeToLocalizedStringTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
|
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(
|
$builder->addModelTransformer(new ReversedTransformer(
|
||||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d')
|
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'Y-m-d')
|
||||||
));
|
));
|
||||||
@ -258,6 +261,7 @@ class DateType extends AbstractType
|
|||||||
|
|
||||||
$resolver->setAllowedValues('input', array(
|
$resolver->setAllowedValues('input', array(
|
||||||
'datetime',
|
'datetime',
|
||||||
|
'datetime_immutable',
|
||||||
'string',
|
'string',
|
||||||
'timestamp',
|
'timestamp',
|
||||||
'array',
|
'array',
|
||||||
|
@ -18,6 +18,7 @@ use Symfony\Component\Form\FormInterface;
|
|||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\ReversedTransformer;
|
use Symfony\Component\Form\ReversedTransformer;
|
||||||
use Symfony\Component\Form\Exception\InvalidConfigurationException;
|
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\DateTimeToStringTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
|
||||||
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
|
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']));
|
$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(
|
$builder->addModelTransformer(new ReversedTransformer(
|
||||||
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s')
|
new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'], 'H:i:s')
|
||||||
));
|
));
|
||||||
@ -252,6 +255,7 @@ class TimeType extends AbstractType
|
|||||||
|
|
||||||
$resolver->setAllowedValues('input', array(
|
$resolver->setAllowedValues('input', array(
|
||||||
'datetime',
|
'datetime',
|
||||||
|
'datetime_immutable',
|
||||||
'string',
|
'string',
|
||||||
'timestamp',
|
'timestamp',
|
||||||
'array',
|
'array',
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
<?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\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());
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,34 @@ class DateTimeTypeTest extends BaseTypeTest
|
|||||||
$this->assertEquals($dateTime, $form->getData());
|
$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()
|
public function testSubmitString()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(static::TESTED_TYPE, null, array(
|
$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());
|
$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()
|
public function testSubmitStringSingleText()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(static::TESTED_TYPE, null, array(
|
$form = $this->factory->create(static::TESTED_TYPE, null, array(
|
||||||
|
@ -105,6 +105,28 @@ class DateTypeTest extends BaseTypeTest
|
|||||||
$this->assertEquals('02.06.2010', $form->getViewData());
|
$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()
|
public function testSubmitFromSingleTextString()
|
||||||
{
|
{
|
||||||
// we test against "de_DE", so we need the full implementation
|
// we test against "de_DE", so we need the full implementation
|
||||||
|
@ -39,6 +39,28 @@ class TimeTypeTest extends BaseTypeTest
|
|||||||
$this->assertEquals($input, $form->getViewData());
|
$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()
|
public function testSubmitString()
|
||||||
{
|
{
|
||||||
$form = $this->factory->create(static::TESTED_TYPE, null, array(
|
$form = $this->factory->create(static::TESTED_TYPE, null, array(
|
||||||
|
Reference in New Issue
Block a user