Explorar o código

Added ability to set "empty_value" for `DateTimeType`, `DateType` and `TimeType`
Additional tests covering added code

stloyd %!s(int64=14) %!d(string=hai) anos
pai
achega
cdd39ac3e2

+ 3 - 1
src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php

@@ -32,12 +32,14 @@ class DateTimeType extends AbstractType
             'years',
             'months',
             'days',
+            'empty_value',
         )));
         $timeOptions = array_intersect_key($options, array_flip(array(
             'hours',
             'minutes',
             'seconds',
             'with_seconds',
+            'empty_value',
         )));
 
         if (isset($options['date_widget'])) {
@@ -97,9 +99,9 @@ class DateTimeType extends AbstractType
     {
         return array(
             'input'         => 'datetime',
-            'with_seconds'  => false,
             'data_timezone' => null,
             'user_timezone' => null,
+            'empty_value'   => null,
             // Don't modify \DateTime classes by reference, we treat
             // them like immutable value objects
             'by_reference'  => false,

+ 28 - 16
src/Symfony/Component/Form/Extension/Core/Type/DateType.php

@@ -41,32 +41,42 @@ class DateType extends AbstractType
             $builder->appendClientTransformer(new DateTimeToLocalizedStringTransformer($options['data_timezone'], $options['user_timezone'], $options['format'], \IntlDateFormatter::NONE));
         } else {
             $yearOptions = $monthOptions = $dayOptions = array();
-            $widget = $options['widget'];
 
-            if ($widget === 'choice') {
+            if ($options['widget'] === 'choice') {
+                if (is_array($options['empty_value'])) {
+                    $options['empty_value'] = array_merge(array('year' => null, 'month' => null, 'day' => null), $options['empty_value']);
+                } else {
+                    $options['empty_value'] = array('year' => $options['empty_value'], 'month' => $options['empty_value'], 'day' => $options['empty_value']);
+                }
+
                 // Only pass a subset of the options to children
                 $yearOptions = array(
                     'choice_list' => new PaddedChoiceList(
                         array_combine($options['years'], $options['years']), 4, '0', STR_PAD_LEFT
                     ),
+                    'empty_value' => $options['empty_value']['year'],
                 );
                 $monthOptions = array(
                     'choice_list' => new MonthChoiceList(
                         $formatter, $options['months']
                     ),
+                    'empty_value' => $options['empty_value']['month'],
                 );
                 $dayOptions = array(
                     'choice_list' => new PaddedChoiceList(
                         array_combine($options['days'], $options['days']), 2, '0', STR_PAD_LEFT
                     ),
+                    'empty_value' => $options['empty_value']['day'],
                 );
             }
 
             $builder
-                ->add('year', $widget, $yearOptions)
-                ->add('month', $widget, $monthOptions)
-                ->add('day', $widget, $dayOptions)
-                ->appendClientTransformer(new DateTimeToArrayTransformer($options['data_timezone'], $options['user_timezone'], array('year', 'month', 'day')))
+                ->add('year', $options['widget'], $yearOptions)
+                ->add('month', $options['widget'], $monthOptions)
+                ->add('day', $options['widget'], $dayOptions)
+                ->appendClientTransformer(new DateTimeToArrayTransformer(
+                    $options['data_timezone'], $options['user_timezone'], array('year', 'month', 'day')
+                ))
             ;
         }
 
@@ -87,6 +97,7 @@ class DateType extends AbstractType
         $builder
             ->setAttribute('formatter', $formatter)
             ->setAttribute('widget', $options['widget'])
+            ->setAttribute('empty_value', $options['empty_value'])
         ;
     }
 
@@ -96,9 +107,9 @@ class DateType extends AbstractType
     public function buildViewBottomUp(FormView $view, FormInterface $form)
     {
         $view->set('widget', $form->getAttribute('widget'));
+        $view->set('empty_value', $form->getAttribute('empty_value'));
 
         if ($view->hasChildren()) {
-
             $pattern = $form->getAttribute('formatter')->getPattern();
 
             // set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy)
@@ -120,17 +131,18 @@ class DateType extends AbstractType
     public function getDefaultOptions(array $options)
     {
         return array(
-            'years'             => range(date('Y') - 5, date('Y') + 5),
-            'months'            => range(1, 12),
-            'days'              => range(1, 31),
-            'widget'            => 'choice',
-            'input'             => 'datetime',
-            'format'            => \IntlDateFormatter::MEDIUM,
-            'data_timezone'     => null,
-            'user_timezone'     => null,
+            'years'         => range(date('Y') - 5, date('Y') + 5),
+            'months'        => range(1, 12),
+            'days'          => range(1, 31),
+            'widget'        => 'choice',
+            'input'         => 'datetime',
+            'format'        => \IntlDateFormatter::MEDIUM,
+            'data_timezone' => null,
+            'user_timezone' => null,
+            'empty_value'   => null,
             // Don't modify \DateTime classes by reference, we treat
             // them like immutable value objects
-            'by_reference'      => false,
+            'by_reference'  => false,
         );
     }
 

+ 37 - 25
src/Symfony/Component/Form/Extension/Core/Type/TimeType.php

@@ -28,30 +28,41 @@ class TimeType extends AbstractType
      */
     public function buildForm(FormBuilder $builder, array $options)
     {
-        $hourOptions = $minuteOptions = $secondOptions = array();
-        $parts = array('hour', 'minute');
-
         if ($options['widget'] === 'choice') {
-            $hourOptions['choice_list'] =  new PaddedChoiceList(
-                array_combine($options['hours'], $options['hours']), 2, '0', STR_PAD_LEFT
-            );
-            $minuteOptions['choice_list'] = new PaddedChoiceList(
-                array_combine($options['minutes'], $options['minutes']), 2, '0', STR_PAD_LEFT
-            );
+            if (is_array($options['empty_value'])) {
+                $options['empty_value'] = array_merge(array('hour' => null, 'minute' => null, 'second' => null), $options['empty_value']);
+            } else {
+                $options['empty_value'] = array('hour' => $options['empty_value'], 'minute' => $options['empty_value'], 'second' => $options['empty_value']);
+            }
+
+            $builder
+                ->add('hour', $options['widget'], array(
+                    'choice_list' => new PaddedChoiceList(
+                        array_combine($options['hours'], $options['hours']), 2, '0', STR_PAD_LEFT
+                    ),
+                    'empty_value' => $options['empty_value']['hour']
+                ))
+                ->add('minute', $options['widget'], array(
+                    'choice_list' => new PaddedChoiceList(
+                        array_combine($options['minutes'], $options['minutes']), 2, '0', STR_PAD_LEFT
+                    ),
+                    'empty_value' => $options['empty_value']['minute']
+                ))
+            ;
 
             if ($options['with_seconds']) {
-                $secondOptions['choice_list'] = new PaddedChoiceList(
-                    array_combine($options['seconds'], $options['seconds']), 2, '0', STR_PAD_LEFT
-                );
+                $builder->add('second', $options['widget'], array(
+                    'choice_list' => new PaddedChoiceList(
+                        array_combine($options['seconds'], $options['seconds']), 2, '0', STR_PAD_LEFT
+                    ),
+                    'empty_value' => $options['empty_value']['second']
+                ));
             }
         }
 
-        $builder->add('hour', $options['widget'], $hourOptions)
-            ->add('minute', $options['widget'], $minuteOptions);
-
+        $parts = array('hour', 'minute');
         if ($options['with_seconds']) {
             $parts[] = 'second';
-            $builder->add('second', $options['widget'], $secondOptions);
         }
 
         if ($options['input'] === 'string') {
@@ -97,17 +108,18 @@ class TimeType extends AbstractType
     public function getDefaultOptions(array $options)
     {
         return array(
-            'hours'             => range(0, 23),
-            'minutes'           => range(0, 59),
-            'seconds'           => range(0, 59),
-            'widget'            => 'choice',
-            'input'             => 'datetime',
-            'with_seconds'      => false,
-            'data_timezone'     => null,
-            'user_timezone'     => null,
+            'hours'         => range(0, 23),
+            'minutes'       => range(0, 59),
+            'seconds'       => range(0, 59),
+            'widget'        => 'choice',
+            'input'         => 'datetime',
+            'with_seconds'  => false,
+            'data_timezone' => null,
+            'user_timezone' => null,
+            'empty_value'   => null,
             // Don't modify \DateTime classes by reference, we treat
             // them like immutable value objects
-            'by_reference'      => false,
+            'by_reference'  => false,
         );
     }
 

+ 185 - 0
tests/Symfony/Tests/Component/Form/AbstractLayoutTest.php

@@ -747,6 +747,87 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
         );
     }
 
+    public function testDateTimeWithEmptyValueGlobal()
+    {
+        $form = $this->factory->createNamed('datetime', 'na&me', null, array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'empty_value' => 'Change&Me',
+            'required' => false,
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./div
+            [@id="na&me_date"]
+            [
+                ./select
+                    [@id="na&me_date_month"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+                /following-sibling::select
+                    [@id="na&me_date_day"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+                /following-sibling::select
+                    [@id="na&me_date_year"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+            ]
+        /following-sibling::div
+            [@id="na&me_time"]
+            [
+                ./select
+                    [@id="na&me_time_hour"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+                /following-sibling::select
+                    [@id="na&me_time_minute"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+            ]
+    ]
+    [count(.//select)=5]
+'
+        );
+    }
+    public function testDateTimeWithEmptyValueOnTime()
+    {
+        $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03', array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'empty_value' => array('hour' => 'Change&Me', 'minute' => 'Change&Me'),
+            'required' => false,
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./div
+            [@id="na&me_date"]
+            [
+                ./select
+                    [@id="na&me_date_month"]
+                    [./option[@value="2"][@selected="selected"]]
+                /following-sibling::select
+                    [@id="na&me_date_day"]
+                    [./option[@value="3"][@selected="selected"]]
+                /following-sibling::select
+                    [@id="na&me_date_year"]
+                    [./option[@value="2011"][@selected="selected"]]
+            ]
+        /following-sibling::div
+            [@id="na&me_time"]
+            [
+                ./select
+                    [@id="na&me_time_hour"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+                /following-sibling::select
+                    [@id="na&me_time_minute"]
+                    [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+            ]
+    ]
+    [count(.//select)=5]
+'
+        );
+    }
+
     public function testDateTimeWithSeconds()
     {
         $form = $this->factory->createNamed('datetime', 'na&me', '2011-02-03 04:05:06', array(
@@ -816,6 +897,62 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
         );
     }
 
+    public function testDateChoiceWithEmptyValueGlobal()
+    {
+        $form = $this->factory->createNamed('date', 'na&me', null, array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'widget' => 'choice',
+            'empty_value' => 'Change&Me',
+            'required' => false,
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./select
+            [@id="na&me_month"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+        /following-sibling::select
+            [@id="na&me_day"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+        /following-sibling::select
+            [@id="na&me_year"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+    ]
+    [count(./select)=3]
+'
+        );
+    }
+
+    public function testDateChoiceWithEmptyValueOnYear()
+    {
+        $form = $this->factory->createNamed('date', 'na&me', null, array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'widget' => 'choice',
+            'required' => false,
+            'empty_value' => array('year' => 'Change&Me'),
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./select
+            [@id="na&me_month"]
+            [./option[@value="1"]]
+        /following-sibling::select
+            [@id="na&me_day"]
+            [./option[@value="1"]]
+        /following-sibling::select
+            [@id="na&me_year"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+    ]
+    [count(./select)=3]
+'
+        );
+    }
+
     public function testDateText()
     {
         $form = $this->factory->createNamed('date', 'na&me', '2011-02-03', array(
@@ -1230,6 +1367,54 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
         );
     }
 
+    public function testTimeWithEmptyValueGlobal()
+    {
+        $form = $this->factory->createNamed('time', 'na&me', null, array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'empty_value' => 'Change&Me',
+            'required' => false,
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./select
+            [@id="na&me_hour"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+        /following-sibling::select
+            [@id="na&me_minute"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+    ]
+    [count(./select)=2]
+'
+        );
+    }
+
+    public function testTimeWithEmptyValueOnYear()
+    {
+        $form = $this->factory->createNamed('time', 'na&me', null, array(
+            'property_path' => 'name',
+            'input' => 'string',
+            'required' => false,
+            'empty_value' => array('hour' => 'Change&Me'),
+        ));
+
+        $this->assertWidgetMatchesXpath($form->createView(), array(),
+'/div
+    [
+        ./select
+            [@id="na&me_hour"]
+            [./option[@value=""][.="[trans]Change&Me[/trans]"]]
+        /following-sibling::select
+            [@id="na&me_minute"]
+            [./option[@value="1"]]
+    ]
+    [count(./select)=2]
+'
+        );
+    }
+
     public function testTimezone()
     {
         $form = $this->factory->createNamed('timezone', 'na&me', 'Europe/Vienna', array(

+ 1 - 1
tests/Symfony/Tests/Component/Form/Extension/Core/Type/DateTypeTest.php

@@ -174,7 +174,7 @@ class DateTypeTest extends LocalizedTestCase
 
         $form->bind($text);
 
-        $this->assertSame(null, $form->getData());
+        $this->assertNull($form->getData());
         $this->assertEquals($text, $form->getClientData());
     }