23
23
use Symfony \Component \Form \ReversedTransformer ;
24
24
use Symfony \Component \OptionsResolver \Options ;
25
25
use Symfony \Component \OptionsResolver \OptionsResolverInterface ;
26
+ use Symfony \Component \OptionsResolver \Exception \InvalidOptionsException ;
26
27
27
28
class DateType extends AbstractType
28
29
{
29
30
const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM ;
30
31
32
+ private static $ acceptedFormats = array (
33
+ \IntlDateFormatter::FULL ,
34
+ \IntlDateFormatter::LONG ,
35
+ \IntlDateFormatter::MEDIUM ,
36
+ \IntlDateFormatter::SHORT ,
37
+ );
38
+
31
39
/**
32
40
* {@inheritdoc}
33
41
*/
34
42
public function buildForm (FormBuilderInterface $ builder , array $ options )
35
43
{
36
- $ format = $ options ['format ' ];
37
- $ pattern = null ;
38
-
39
- $ allowedFormats = array (
40
- \IntlDateFormatter::FULL ,
41
- \IntlDateFormatter::LONG ,
42
- \IntlDateFormatter::MEDIUM ,
43
- \IntlDateFormatter::SHORT ,
44
- );
45
-
46
- // If $format is not in the allowed options, it's considered as the pattern of the formatter if it is a string
47
- if (!in_array ($ format , $ allowedFormats , true )) {
48
- if (is_string ($ format )) {
49
- $ format = self ::DEFAULT_FORMAT ;
50
- $ pattern = $ options ['format ' ];
51
- } else {
52
- throw new CreationException ('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom pattern ' );
53
- }
44
+ $ dateFormat = is_int ($ options ['format ' ]) ? $ options ['format ' ] : self ::DEFAULT_FORMAT ;
45
+ $ timeFormat = \IntlDateFormatter::NONE ;
46
+ $ calendar = \IntlDateFormatter::GREGORIAN ;
47
+ $ pattern = is_string ($ options ['format ' ]) ? $ options ['format ' ] : null ;
48
+
49
+ if (!in_array ($ dateFormat , self ::$ acceptedFormats , true )) {
50
+ throw new InvalidOptionsException ('The "format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format. ' );
54
51
}
55
52
56
- $ formatter = new \IntlDateFormatter (
57
- \Locale::getDefault (),
58
- $ format ,
59
- \IntlDateFormatter::NONE ,
60
- 'UTC ' ,
61
- \IntlDateFormatter::GREGORIAN ,
62
- $ pattern
63
- );
64
- $ formatter ->setLenient (false );
53
+ if (null !== $ pattern && (false === strpos ($ pattern , 'y ' ) || false === strpos ($ pattern , 'M ' ) || false === strpos ($ pattern , 'd ' ))) {
54
+ throw new InvalidOptionsException (sprintf ('The "format" option should contain the patterns "y", "M" and "d". Its current value is "%s". ' , $ pattern ));
55
+ }
65
56
66
57
if ('single_text ' === $ options ['widget ' ]) {
67
- $ builder ->addViewTransformer (new DateTimeToLocalizedStringTransformer ($ options ['data_timezone ' ], $ options ['user_timezone ' ], $ format , \IntlDateFormatter::NONE , \IntlDateFormatter::GREGORIAN , $ pattern ));
58
+ $ builder ->addViewTransformer (new DateTimeToLocalizedStringTransformer (
59
+ $ options ['data_timezone ' ],
60
+ $ options ['user_timezone ' ],
61
+ $ dateFormat ,
62
+ $ timeFormat ,
63
+ $ calendar ,
64
+ $ pattern
65
+ ));
68
66
} else {
69
67
$ yearOptions = $ monthOptions = $ dayOptions = array ();
70
68
71
- if ('choice ' === $ options ['widget ' ]) {
72
- $ years = $ months = $ days = array ();
73
-
74
- foreach ($ options ['years ' ] as $ year ) {
75
- $ years [$ year ] = str_pad ($ year , 4 , '0 ' , STR_PAD_LEFT );
76
- }
77
- foreach ($ options ['months ' ] as $ month ) {
78
- $ months [$ month ] = str_pad ($ month , 2 , '0 ' , STR_PAD_LEFT );
79
- }
80
- foreach ($ options ['days ' ] as $ day ) {
81
- $ days [$ day ] = str_pad ($ day , 2 , '0 ' , STR_PAD_LEFT );
82
- }
69
+ $ formatter = new \IntlDateFormatter (
70
+ \Locale::getDefault (),
71
+ $ dateFormat ,
72
+ $ timeFormat ,
73
+ 'UTC ' ,
74
+ $ calendar ,
75
+ $ pattern
76
+ );
77
+ $ formatter ->setLenient (false );
83
78
79
+ if ('choice ' === $ options ['widget ' ]) {
84
80
// Only pass a subset of the options to children
85
81
$ yearOptions = array (
86
- 'choices ' => $ years ,
82
+ 'choices ' => $ this -> formatTimestamps ( $ formatter , ' /y+/ ' , $ this -> listYears ( $ options [ ' years ' ])) ,
87
83
'empty_value ' => $ options ['empty_value ' ]['year ' ],
88
84
);
89
85
$ monthOptions = array (
90
- 'choices ' => $ this ->formatMonths ($ formatter , $ months ),
86
+ 'choices ' => $ this ->formatTimestamps ($ formatter , ' /M+/ ' , $ this -> listMonths ( $ options [ ' months ' ]) ),
91
87
'empty_value ' => $ options ['empty_value ' ]['month ' ],
92
88
);
93
89
$ dayOptions = array (
94
- 'choices ' => $ days ,
90
+ 'choices ' => $ this -> formatTimestamps ( $ formatter , ' /d+/ ' , $ this -> listDays ( $ options [ ' days ' ])) ,
95
91
'empty_value ' => $ options ['empty_value ' ]['day ' ],
96
92
);
93
+ }
97
94
98
- // Append generic carry-along options
99
- foreach (array ('required ' , 'translation_domain ' ) as $ passOpt ) {
100
- $ yearOptions [$ passOpt ] = $ monthOptions [$ passOpt ] = $ dayOptions [$ passOpt ] = $ options [$ passOpt ];
101
- }
95
+ // Append generic carry-along options
96
+ foreach (array ('required ' , 'translation_domain ' ) as $ passOpt ) {
97
+ $ yearOptions [$ passOpt ] = $ monthOptions [$ passOpt ] = $ dayOptions [$ passOpt ] = $ options [$ passOpt ];
102
98
}
103
99
104
100
$ builder
@@ -108,6 +104,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
108
104
->addViewTransformer (new DateTimeToArrayTransformer (
109
105
$ options ['data_timezone ' ], $ options ['user_timezone ' ], array ('year ' , 'month ' , 'day ' )
110
106
))
107
+ ->setAttribute ('formatter ' , $ formatter )
111
108
;
112
109
}
113
110
@@ -124,8 +121,6 @@ public function buildForm(FormBuilderInterface $builder, array $options)
124
121
new DateTimeToArrayTransformer ($ options ['data_timezone ' ], $ options ['data_timezone ' ], array ('year ' , 'month ' , 'day ' ))
125
122
));
126
123
}
127
-
128
- $ builder ->setAttribute ('formatter ' , $ formatter );
129
124
}
130
125
131
126
/**
@@ -139,7 +134,7 @@ public function finishView(FormViewInterface $view, FormInterface $form, array $
139
134
$ view ->setVar ('type ' , 'date ' );
140
135
}
141
136
142
- if (count ( $ view ) > 0 ) {
137
+ if ($ form -> getConfig ()-> hasAttribute ( ' formatter ' ) ) {
143
138
$ pattern = $ form ->getConfig ()->getAttribute ('formatter ' )->getPattern ();
144
139
145
140
// set right order with respect to locale (e.g.: de_DE=dd.MM.yy; en_US=M/d/yy)
@@ -224,6 +219,10 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
224
219
'choice ' ,
225
220
),
226
221
));
222
+
223
+ $ resolver ->setAllowedTypes (array (
224
+ 'format ' => array ('int ' , 'string ' ),
225
+ ));
227
226
}
228
227
229
228
/**
@@ -242,18 +241,18 @@ public function getName()
242
241
return 'date ' ;
243
242
}
244
243
245
- private function formatMonths (\IntlDateFormatter $ formatter , array $ months )
244
+ private function formatTimestamps (\IntlDateFormatter $ formatter , $ regex , array $ timestamps )
246
245
{
247
246
$ pattern = $ formatter ->getPattern ();
248
247
$ timezone = $ formatter ->getTimezoneId ();
249
248
250
249
$ formatter ->setTimezoneId (\DateTimeZone::UTC );
251
250
252
- if (preg_match (' /M+/ ' , $ pattern , $ matches )) {
251
+ if (preg_match ($ regex , $ pattern , $ matches )) {
253
252
$ formatter ->setPattern ($ matches [0 ]);
254
253
255
- foreach ($ months as $ key => $ value ) {
256
- $ months [$ key ] = $ formatter ->format (gmmktime ( 0 , 0 , 0 , $ key , 15 ) );
254
+ foreach ($ timestamps as $ key => $ timestamp ) {
255
+ $ timestamps [$ key ] = $ formatter ->format ($ timestamp );
257
256
}
258
257
259
258
// I'd like to clone the formatter above, but then we get a
@@ -263,6 +262,39 @@ private function formatMonths(\IntlDateFormatter $formatter, array $months)
263
262
264
263
$ formatter ->setTimezoneId ($ timezone );
265
264
266
- return $ months ;
265
+ return $ timestamps ;
266
+ }
267
+
268
+ private function listYears (array $ years )
269
+ {
270
+ $ result = array ();
271
+
272
+ foreach ($ years as $ year ) {
273
+ $ result [$ year ] = gmmktime (0 , 0 , 0 , 6 , 15 , $ year );
274
+ }
275
+
276
+ return $ result ;
277
+ }
278
+
279
+ private function listMonths (array $ months )
280
+ {
281
+ $ result = array ();
282
+
283
+ foreach ($ months as $ month ) {
284
+ $ result [$ month ] = gmmktime (0 , 0 , 0 , $ month , 15 );
285
+ }
286
+
287
+ return $ result ;
288
+ }
289
+
290
+ private function listDays (array $ days )
291
+ {
292
+ $ result = array ();
293
+
294
+ foreach ($ days as $ day ) {
295
+ $ result [$ day ] = gmmktime (0 , 0 , 0 , 5 , $ day );
296
+ }
297
+
298
+ return $ result ;
267
299
}
268
300
}
0 commit comments