diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php index c3f95f59af..64d2c2d2f4 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php @@ -90,6 +90,9 @@ class DateTimeType extends AbstractType )); } } else { + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); // Only pass a subset of the options to children $dateOptions = array_intersect_key($options, array_flip(array( 'years', @@ -105,6 +108,10 @@ class DateTimeType extends AbstractType 'invalid_message_parameters', ))); + if (isset($emptyData['date'])) { + $dateOptions['empty_data'] = $emptyData['date']; + } + $timeOptions = array_intersect_key($options, array_flip(array( 'hours', 'minutes', @@ -121,6 +128,10 @@ class DateTimeType extends AbstractType 'invalid_message_parameters', ))); + if (isset($emptyData['time'])) { + $timeOptions['empty_data'] = $emptyData['time']; + } + if (false === $options['label']) { $dateOptions['label'] = false; $timeOptions['label'] = false; @@ -226,6 +237,9 @@ class DateTimeType extends AbstractType // this option. 'data_class' => null, 'compound' => $compound, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, )); // Don't add some defaults in order to preserve the defaults diff --git a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php index ad4499899a..ce41c3c7d3 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/DateType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/DateType.php @@ -75,7 +75,21 @@ class DateType extends AbstractType $yearOptions = $monthOptions = $dayOptions = array( 'error_bubbling' => true, + 'empty_data' => '', ); + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); + + if (isset($emptyData['year'])) { + $yearOptions['empty_data'] = $emptyData['year']; + } + if (isset($emptyData['month'])) { + $monthOptions['empty_data'] = $emptyData['month']; + } + if (isset($emptyData['day'])) { + $dayOptions['empty_data'] = $emptyData['day']; + } if (isset($options['invalid_message'])) { $dayOptions['invalid_message'] = $options['invalid_message']; @@ -272,6 +286,9 @@ class DateType extends AbstractType // this option. 'data_class' => null, 'compound' => $compound, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, 'choice_translation_domain' => false, )); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index 4a27cd440b..facfc098b7 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -70,7 +70,15 @@ class TimeType extends AbstractType } else { $hourOptions = $minuteOptions = $secondOptions = array( 'error_bubbling' => true, + 'empty_data' => '', ); + // when the form is compound the entries of the array are ignored in favor of children data + // so we need to handle the cascade setting here + $emptyData = $builder->getEmptyData() ?: array(); + + if (isset($emptyData['hour'])) { + $hourOptions['empty_data'] = $emptyData['hour']; + } if (isset($options['invalid_message'])) { $hourOptions['invalid_message'] = $options['invalid_message']; @@ -138,10 +146,16 @@ class TimeType extends AbstractType $builder->add('hour', self::$widgets[$options['widget']], $hourOptions); if ($options['with_minutes']) { + if (isset($emptyData['minute'])) { + $minuteOptions['empty_data'] = $emptyData['minute']; + } $builder->add('minute', self::$widgets[$options['widget']], $minuteOptions); } if ($options['with_seconds']) { + if (isset($emptyData['second'])) { + $secondOptions['empty_data'] = $emptyData['second']; + } $builder->add('second', self::$widgets[$options['widget']], $secondOptions); } @@ -265,6 +279,9 @@ class TimeType extends AbstractType // representation is not \DateTime, but an array, we need to unset // this option. 'data_class' => null, + 'empty_data' => function (Options $options) { + return $options['compound'] ? array() : ''; + }, 'compound' => $compound, 'choice_translation_domain' => false, )); 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 2531332f05..f0e45f5d56 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTimeTypeTest.php @@ -631,4 +631,31 @@ class DateTimeTypeTest extends BaseTypeTest $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } + + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'widget' => $widget, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i', '2018-11-11 21:23'); + + return array( + 'Simple field' => array('single_text', '2018-11-11T21:23:00', $expectedData), + 'Compound text field' => array('text', array('date' => array('year' => '2018', 'month' => '11', 'day' => '11'), 'time' => array('hour' => '21', 'minute' => '23')), $expectedData), + 'Compound choice field' => array('choice', array('date' => array('year' => '2018', 'month' => '11', 'day' => '11'), 'time' => array('hour' => '21', 'minute' => '23')), $expectedData), + ); + } } 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 0f03b1851d..a15a6c2b32 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -1011,25 +1011,36 @@ class DateTypeTest extends BaseTypeTest )); $form->submit(null); - // view transformer write back empty strings in the view data + // view transformer writes back empty strings in the view data $this->assertSame(array('year' => '', 'month' => '', 'day' => ''), $form->getViewData()); $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } - public function testSingleTextSubmitNullUsesDefaultEmptyData() + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) { - $emptyData = '2018-11-11'; $form = $this->factory->create(static::TESTED_TYPE, null, array( - 'widget' => 'single_text', + 'widget' => $widget, 'empty_data' => $emptyData, )); $form->submit(null); - $date = new \DateTime($emptyData); - $this->assertSame($emptyData, $form->getViewData()); - $this->assertEquals($date, $form->getNormData()); - $this->assertEquals($date, $form->getData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i:s', '2018-11-11 00:00:00'); + + return array( + 'Simple field' => array('single_text', '2018-11-11', $expectedData), + 'Compound text fields' => array('text', array('year' => '2018', 'month' => '11', 'day' => '11'), $expectedData), + 'Compound choice fields' => array('choice', array('year' => '2018', 'month' => '11', 'day' => '11'), $expectedData), + ); } } 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 98d4f902fa..5ed4a236f1 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -805,9 +805,36 @@ class TimeTypeTest extends BaseTypeTest )); $form->submit(null); - // view transformer write back empty strings in the view data + // view transformer writes back empty strings in the view data $this->assertSame(array('hour' => '', 'minute' => ''), $form->getViewData()); $this->assertSame($expectedData, $form->getNormData()); $this->assertSame($expectedData, $form->getData()); } + + /** + * @dataProvider provideEmptyData + */ + public function testSubmitNullUsesDateEmptyData($widget, $emptyData, $expectedData) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'widget' => $widget, + 'empty_data' => $emptyData, + )); + $form->submit(null); + + $this->assertSame($emptyData, $form->getViewData()); + $this->assertEquals($expectedData, $form->getNormData()); + $this->assertEquals($expectedData, $form->getData()); + } + + public function provideEmptyData() + { + $expectedData = \DateTime::createFromFormat('Y-m-d H:i', '1970-01-01 21:23'); + + return array( + 'Simple field' => array('single_text', '21:23', $expectedData), + 'Compound text field' => array('text', array('hour' => '21', 'minute' => '23'), $expectedData), + 'Compound choice field' => array('choice', array('hour' => '21', 'minute' => '23'), $expectedData), + ); + } }