]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/form/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / form / Symfony / Component / Form / Extension / Core / Type / DateTimeType.php
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\Form\Extension\Core\Type;
13
14 use Symfony\Component\Form\AbstractType;
15 use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
16 use Symfony\Component\Form\FormInterface;
17 use Symfony\Component\Form\FormBuilderInterface;
18 use Symfony\Component\Form\FormView;
19 use Symfony\Component\Form\ReversedTransformer;
20 use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
21 use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
22 use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
23 use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
24 use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
25 use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer;
26 use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
27 use Symfony\Component\OptionsResolver\Options;
28 use Symfony\Component\OptionsResolver\OptionsResolverInterface;
29
30 class DateTimeType extends AbstractType
31 {
32 const DEFAULT_DATE_FORMAT = \IntlDateFormatter::MEDIUM;
33
34 const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM;
35
36 /**
37 * This is not quite the HTML5 format yet, because ICU lacks the
38 * capability of parsing and generating RFC 3339 dates, which
39 * are like the below pattern but with a timezone suffix. The
40 * timezone suffix is
41 *
42 * * "Z" for UTC
43 * * "(-|+)HH:mm" for other timezones (note the colon!)
44 *
45 * For more information see:
46 *
47 * http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax
48 * http://www.w3.org/TR/html-markup/input.datetime.html
49 * http://tools.ietf.org/html/rfc3339
50 *
51 * An ICU ticket was created:
52 * http://icu-project.org/trac/ticket/9421
53 *
54 * It was supposedly fixed, but is not available in all PHP installations
55 * yet. To temporarily circumvent this issue, DateTimeToRfc3339Transformer
56 * is used when the format matches this constant.
57 */
58 const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZZZZ";
59
60 private static $acceptedFormats = array(
61 \IntlDateFormatter::FULL,
62 \IntlDateFormatter::LONG,
63 \IntlDateFormatter::MEDIUM,
64 \IntlDateFormatter::SHORT,
65 );
66
67 /**
68 * {@inheritdoc}
69 */
70 public function buildForm(FormBuilderInterface $builder, array $options)
71 {
72 $parts = array('year', 'month', 'day', 'hour');
73 $dateParts = array('year', 'month', 'day');
74 $timeParts = array('hour');
75
76 if ($options['with_minutes']) {
77 $parts[] = 'minute';
78 $timeParts[] = 'minute';
79 }
80
81 if ($options['with_seconds']) {
82 $parts[] = 'second';
83 $timeParts[] = 'second';
84 }
85
86 $dateFormat = is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT;
87 $timeFormat = self::DEFAULT_TIME_FORMAT;
88 $calendar = \IntlDateFormatter::GREGORIAN;
89 $pattern = is_string($options['format']) ? $options['format'] : null;
90
91 if (!in_array($dateFormat, self::$acceptedFormats, true)) {
92 throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
93 }
94
95 if ('single_text' === $options['widget']) {
96 if (self::HTML5_FORMAT === $pattern) {
97 $builder->addViewTransformer(new DateTimeToRfc3339Transformer(
98 $options['model_timezone'],
99 $options['view_timezone']
100 ));
101 } else {
102 $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
103 $options['model_timezone'],
104 $options['view_timezone'],
105 $dateFormat,
106 $timeFormat,
107 $calendar,
108 $pattern
109 ));
110 }
111 } else {
112 // Only pass a subset of the options to children
113 $dateOptions = array_intersect_key($options, array_flip(array(
114 'years',
115 'months',
116 'days',
117 'empty_value',
118 'required',
119 'translation_domain',
120 )));
121
122 $timeOptions = array_intersect_key($options, array_flip(array(
123 'hours',
124 'minutes',
125 'seconds',
126 'with_minutes',
127 'with_seconds',
128 'empty_value',
129 'required',
130 'translation_domain',
131 )));
132
133 if (null !== $options['date_widget']) {
134 $dateOptions['widget'] = $options['date_widget'];
135 }
136
137 if (null !== $options['time_widget']) {
138 $timeOptions['widget'] = $options['time_widget'];
139 }
140
141 if (null !== $options['date_format']) {
142 $dateOptions['format'] = $options['date_format'];
143 }
144
145 $dateOptions['input'] = $timeOptions['input'] = 'array';
146 $dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true;
147
148 $builder
149 ->addViewTransformer(new DataTransformerChain(array(
150 new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts),
151 new ArrayToPartsTransformer(array(
152 'date' => $dateParts,
153 'time' => $timeParts,
154 )),
155 )))
156 ->add('date', 'date', $dateOptions)
157 ->add('time', 'time', $timeOptions)
158 ;
159 }
160
161 if ('string' === $options['input']) {
162 $builder->addModelTransformer(new ReversedTransformer(
163 new DateTimeToStringTransformer($options['model_timezone'], $options['model_timezone'])
164 ));
165 } elseif ('timestamp' === $options['input']) {
166 $builder->addModelTransformer(new ReversedTransformer(
167 new DateTimeToTimestampTransformer($options['model_timezone'], $options['model_timezone'])
168 ));
169 } elseif ('array' === $options['input']) {
170 $builder->addModelTransformer(new ReversedTransformer(
171 new DateTimeToArrayTransformer($options['model_timezone'], $options['model_timezone'], $parts)
172 ));
173 }
174 }
175
176 /**
177 * {@inheritdoc}
178 */
179 public function buildView(FormView $view, FormInterface $form, array $options)
180 {
181 $view->vars['widget'] = $options['widget'];
182
183 // Change the input to a HTML5 date input if
184 // * the widget is set to "single_text"
185 // * the format matches the one expected by HTML5
186 if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
187 $view->vars['type'] = 'datetime';
188 }
189 }
190
191 /**
192 * {@inheritdoc}
193 */
194 public function setDefaultOptions(OptionsResolverInterface $resolver)
195 {
196 $compound = function (Options $options) {
197 return $options['widget'] !== 'single_text';
198 };
199
200 // Defaults to the value of "widget"
201 $dateWidget = function (Options $options) {
202 return $options['widget'];
203 };
204
205 // Defaults to the value of "widget"
206 $timeWidget = function (Options $options) {
207 return $options['widget'];
208 };
209
210 $resolver->setDefaults(array(
211 'input' => 'datetime',
212 'model_timezone' => null,
213 'view_timezone' => null,
214 'format' => self::HTML5_FORMAT,
215 'date_format' => null,
216 'widget' => null,
217 'date_widget' => $dateWidget,
218 'time_widget' => $timeWidget,
219 'with_minutes' => true,
220 'with_seconds' => false,
221 // Don't modify \DateTime classes by reference, we treat
222 // them like immutable value objects
223 'by_reference' => false,
224 'error_bubbling' => false,
225 // If initialized with a \DateTime object, FormType initializes
226 // this option to "\DateTime". Since the internal, normalized
227 // representation is not \DateTime, but an array, we need to unset
228 // this option.
229 'data_class' => null,
230 'compound' => $compound,
231 ));
232
233 // Don't add some defaults in order to preserve the defaults
234 // set in DateType and TimeType
235 $resolver->setOptional(array(
236 'empty_value',
237 'years',
238 'months',
239 'days',
240 'hours',
241 'minutes',
242 'seconds',
243 ));
244
245 $resolver->setAllowedValues(array(
246 'input' => array(
247 'datetime',
248 'string',
249 'timestamp',
250 'array',
251 ),
252 'date_widget' => array(
253 null, // inherit default from DateType
254 'single_text',
255 'text',
256 'choice',
257 ),
258 'time_widget' => array(
259 null, // inherit default from TimeType
260 'single_text',
261 'text',
262 'choice',
263 ),
264 // This option will overwrite "date_widget" and "time_widget" options
265 'widget' => array(
266 null, // default, don't overwrite options
267 'single_text',
268 'text',
269 'choice',
270 ),
271 ));
272 }
273
274 /**
275 * {@inheritdoc}
276 */
277 public function getName()
278 {
279 return 'datetime';
280 }
281 }