1: <?php
2: namespace TIC\FormBundle\Form\Type;
3:
4: #use Symfony\Component\Form\AbstractType as BaseType;
5: use TIC\FormBundle\Base\TICWidgetType as BaseType;
6:
7: use Symfony\Component\OptionsResolver\OptionsResolver;
8: use Symfony\Component\OptionsResolver\Options;
9: use Symfony\Component\Form\FormBuilderInterface;
10: use Symfony\Component\Form\FormInterface;
11: use Symfony\Component\Form\FormView;
12:
13: /**
14: * Sélecteur de date (popup avec calendrier).
15: * Intégration du composant JS bootstrap-datetimepicker
16: */
17: class DateType extends BaseType
18: {
19: protected $form_parent = \Symfony\Component\Form\Extension\Core\Type\DateType::class;
20: protected $default_prefix = '<calendar>';
21: protected $default_suffix = '<calendar>';
22: /** Formattage standard forcé coté PHP (transformations selon la locale laissée au composant JS) */
23: const INTERNAL_DATE_FORMAT = "yyyy-MM-dd";
24:
25:
26: /**
27: * {@inheritdoc}
28: */
29: public function configureOptions(OptionsResolver $resolver): void
30: {
31: parent::configureOptions($resolver);
32:
33: $resolver->setDefaults(array(
34: 'placeholder' => false, // true, false, String (ex: "widget.date.ph_format_d")
35: 'suffix' => true,
36:
37: 'compound' => false, // non négociable !
38: 'widget' => 'single_text', // non négociable !
39: 'format' => self::INTERNAL_DATE_FORMAT, // non négociable !
40: 'immutable' => false,
41: 'minDate' => null,
42: 'maxDate' => null,
43:
44: 'with_js' => true, // utilise le Bootstrap DatePicker, sinon champs HTML5
45: 'jsformat' => 'L', // cf http://momentjs.com/docs/#/displaying/format/
46: 'culture' => \Locale::getPrimaryLanguage(\Locale::getDefault()),
47: 'viewDate' => null, // date par défaut du sélecteur (mais non sélectionnée)
48: 'viewMode' => 'days', // 'days', 'months', 'years' or 'decades'
49: 'buttons' => true, // array('today','clear','close') | true=ALL | false=NONE
50: ));
51:
52: $resolver->setAllowedTypes('immutable', array('bool'));
53: $resolver->setAllowedTypes('with_js', array('bool'));
54: $resolver->setAllowedTypes('jsformat', array('string'));
55: $resolver->setAllowedTypes('minDate', array('null', 'datetime', 'timestamp', 'int', 'string', 'boolean'));
56: $resolver->setAllowedTypes('maxDate', array('null', 'datetime', 'timestamp', 'int', 'string', 'boolean'));
57: $resolver->setAllowedTypes('viewDate', array('null', 'datetime', 'timestamp', 'int', 'string'));
58: $resolver->setAllowedTypes('viewMode', array('string'));
59: $resolver->setAllowedTypes('buttons', array('bool', 'array'));
60:
61: $resolver->setAllowedValues('compound', false);
62: $resolver->setAllowedValues('widget', 'single_text');
63: $resolver->setAllowedValues('format', self::INTERNAL_DATE_FORMAT);
64: $resolver->setAllowedValues('viewMode', array('days', 'months', 'years', 'decades'));
65:
66: $resolver->setNormalizer('input', function (Options $options, $input) {
67: return $options['immutable'] ? 'datetime_immutable' : 'datetime';
68: });
69: $resolver->setNormalizer('html5', function (Options $options, $html5) {
70: return ! $options['with_js'];
71: });
72: $resolver->setNormalizer('minDate', function (Options $options, $minDate) {
73: if (is_bool($minDate)) $minDate = ($minDate) ? new \DateTime() : null;
74: return $minDate;
75: });
76: $resolver->setNormalizer('maxDate', function (Options $options, $maxDate) {
77: if (is_bool($maxDate)) $maxDate = ($maxDate) ? new \DateTime() : null;
78: return $maxDate;
79: });
80: $resolver->setNormalizer('buttons', function (Options $options, $buttons) {
81: if (is_bool($buttons)) $buttons = ($buttons) ? array('today', 'clear', 'close') : array();
82: return $buttons;
83: });
84:
85: // annulation du normalizer appliqué dans le champs parent DateType
86: $resolver->setNormalizer('placeholder', function (Options $options, $value) { return $value; });
87: }
88:
89:
90: /**
91: * {@inheritdoc}
92: */
93: public function finishView(FormView $view, FormInterface $form, array $options): void
94: {
95: // valeurs min/max avec correction éventuelle de cohérence
96: $view->vars['minDate'] = $this->readDateOption($options['minDate']);
97: $view->vars['maxDate'] = $this->readDateOption($options['maxDate']);
98: if (($view->vars['minDate'] !== null) && ($view->vars['maxDate'] !== null)) {
99: if (strcmp($view->vars['maxDate'], $view->vars['minDate'])<0) $view->vars['maxDate'] = $view->vars['minDate'];
100: }
101:
102: // valeurs utilisées uniquement en mode JS par le Bootstrap DateTimePicker
103: $view->vars['with_js'] = $options['with_js'];
104: if ($options['with_js']) {
105: $view->vars['format'] = $options['jsformat'];
106: $view->vars['culture'] = $options['culture'];
107: $view->vars['buttons'] = $options['buttons'];
108: $view->vars['viewMode'] = $options['viewMode'];
109: $view->vars['viewDate'] = $this->readDateOption($options['viewDate'], true);
110:
111: if ($view->vars['minDate'] !== null) {
112: if (($view->vars['value'] !== null) && (strcmp($view->vars['value'], $view->vars['minDate'])<0)) $view->vars['value'] = null;
113: if (($view->vars['viewDate'] !== null) && (strcmp($view->vars['viewDate'],$view->vars['minDate'])<0)) $view->vars['viewDate']= $view->vars['minDate'];
114: }
115: if ($view->vars['maxDate'] !== null) {
116: if (($view->vars['value'] !== null) && (strcmp($view->vars['value'], $view->vars['maxDate'])>0)) $view->vars['value'] = null;
117: if (($view->vars['viewDate'] !== null) && (strcmp($view->vars['viewDate'],$view->vars['maxDate'])>0)) $view->vars['viewDate']= $view->vars['maxDate'];
118: }
119: $view->vars['jsValue'] = empty($view->vars['value']) ? 'false' : 'new Date("'.$view->vars['value'].'")';
120: }
121:
122: // spécification de la taille du widget (ajout de classe bootstrap)
123: $this->setViewInputSizing($view);
124: }
125:
126:
127: /**
128: * Conversion d'un paramètre de date.
129: * @param mixed $dateval Valeur timestamp | objet Datetime | chaine 'YYYY-MM-DD'
130: * @param string $default Valeur par défaut si $dateval est nul
131: * @return string Date dans une chaine au format 'YYYY-MM-DD'
132: */
133: protected function readDateOption($dateval, $default = null)
134: {
135: if ($dateval === null) return ($default === true) ? date('Y-m-d') : $default;
136: if (is_numeric($dateval)) return date('Y-m-d', $dateval);
137: if (is_object($dateval)) return $dateval->format('Y-m-d');
138: return $dateval;
139: }
140:
141:
142: }
143: