CarbonType   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 192
ccs 70
cts 70
cp 1
rs 10
c 0
b 0
f 0
wmc 23

8 Methods

Rating   Name   Duplication   Size   Complexity  
A buildView() 0 6 4
A addArrayViewTransformer() 0 19 4
A buildForm() 0 10 2
A initParts() 0 14 3
A addViewTransformer() 0 6 2
A addSingleTextViewTransformer() 0 20 4
A addModelTransformer() 0 8 2
A checkDateFormat() 0 5 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Xgc\CarbonBundle\Form\Type;
5
6
use IntlDateFormatter;
7
use Symfony\Component\Form\Exception\InvalidArgumentException;
8
use Symfony\Component\Form\Exception\UnexpectedTypeException;
9
use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
10
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
11
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
12
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer;
13
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
14
use Symfony\Component\Form\Extension\Core\Type\DateType;
15
use Symfony\Component\Form\Extension\Core\Type\TimeType;
16
use Symfony\Component\Form\FormBuilderInterface;
17
use Symfony\Component\Form\FormInterface;
18
use Symfony\Component\Form\FormView;
19
use Symfony\Component\Form\ReversedTransformer;
20
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
21
use Xgc\CarbonBundle\Form\DataTransformer\CarbonToArrayTransformer;
22
use Xgc\CarbonBundle\Form\DataTransformer\CarbonToDateTimeTransformer;
23
24
/**
25
 * Class CarbonType
26
 *
27
 * @package Xgc\XgcCarbonBundle\Form\Type
28
 */
29
class CarbonType extends DateTimeType
30
{
31
32
    private static $acceptedFormats = [
33
        IntlDateFormatter::FULL,
34
        IntlDateFormatter::LONG,
35
        IntlDateFormatter::MEDIUM,
36
        IntlDateFormatter::SHORT
37
    ];
38
39
    private static $dateOptions = [
40
        'years', 'months', 'days',
41
        'placeholder',
42
        'choice_translation_domain',
43
        'required',
44
        'translation_domain',
45
        'html5',
46
        'invalid_message',
47
        'invalid_message_parameters'
48
    ];
49
50
    private static $timeOptions = [
51
        'hours', 'minutes', 'seconds',
52
        'with_minutes', 'with_seconds',
53
        'placeholder',
54
        'choice_translation_domain',
55
        'required',
56
        'translation_domain',
57
        'html5',
58
        'invalid_message',
59
        'invalid_message_parameters'
60
    ];
61
62
    private $parts;
63
    private $dateParts;
64
    private $timeParts;
65
66
    /**
67
     * @param FormBuilderInterface $builder
68
     * @param array                $options
69
     *
70
     * @throws InvalidArgumentException
71
     * @throws InvalidOptionsException
72
     * @throws UnexpectedTypeException
73
     */
74 2
    public function buildForm(FormBuilderInterface $builder, array $options): void
75
    {
76 2
        $this->initParts($options);
77
78 2
        $options['widget'] = $options['widget'] ?? 'choice';
79
80 2
        $dateFormat = \is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT;
81 2
        $this->checkDateFormat($dateFormat);
82 1
        $this->addViewTransformer($builder, $options);
83 1
        $this->addModelTransformer($builder, $options);
84 1
    }
85
86
    /**
87
     * @param array $options
88
     */
89 2
    private function initParts(array $options)
90
    {
91 2
        $this->parts     = ['year', 'month', 'day', 'hour'];
92 2
        $this->dateParts = ['year', 'month', 'day'];
93 2
        $this->timeParts = ['hour'];
94
95 2
        if ($options['with_minutes']) {
96 2
            $this->parts[]     = 'minute';
97 2
            $this->timeParts[] = 'minute';
98
        }
99
100 2
        if ($options['with_seconds']) {
101 2
            $this->parts[]     = 'second';
102 2
            $this->timeParts[] = 'second';
103
        }
104 2
    }
105
106
    /**
107
     * @param FormBuilderInterface $builder
108
     * @param array                $options
109
     *
110
     * @throws InvalidArgumentException
111
     */
112 1
    private function addViewTransformer(FormBuilderInterface $builder, array $options)
113
    {
114 1
        if ($options['widget'] === 'single_text') {
115 1
            $this->addSingleTextViewTransformer($builder, $options);
116
        } else {
117 1
            $this->addArrayViewTransformer($builder, $options);
118
        }
119 1
    }
120
121
    /**
122
     * @param FormBuilderInterface $builder
123
     * @param array                $options
124
     *
125
     * @throws UnexpectedTypeException
126
     */
127 1
    private function addArrayViewTransformer(FormBuilderInterface $builder, array $options)
128
    {
129 1
        $dateOptions = \array_intersect_key($options, \array_flip(self::$dateOptions));
130 1
        $timeOptions = \array_intersect_key($options, \array_flip(self::$timeOptions));
131
132 1
        $options['date_widget'] === null ?: $dateOptions['widget'] = $options['date_widget'];
133 1
        $options['time_widget'] === null ?: $dateOptions['widget'] = $options['time_widget'];
134 1
        $options['date_format'] === null ?: $dateOptions['format'] = $options['date_format'];
135
136 1
        $dateOptions['input']          = $timeOptions['input'] = 'array';
137 1
        $dateOptions['error_bubbling'] = $timeOptions['error_bubbling'] = true;
138
139
        $builder
140 1
            ->addViewTransformer(new DataTransformerChain([
141 1
                new CarbonToArrayTransformer($options['model_timezone'], $options['view_timezone'], $this->parts),
142 1
                new ArrayToPartsTransformer(['date' => $this->dateParts, 'time' => $this->timeParts])
143
            ]))
144 1
            ->add('date', DateType::class, $dateOptions)
145 1
            ->add('time', TimeType::class, $timeOptions);
146 1
    }
147
148
    /**
149
     * @param FormBuilderInterface $builder
150
     * @param array                $options
151
     *
152
     * @throws InvalidArgumentException
153
     */
154 1
    private function addSingleTextViewTransformer(FormBuilderInterface $builder, array $options)
155
    {
156 1
        $dateFormat = \is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT;
157 1
        $timeFormat = self::DEFAULT_TIME_FORMAT;
158 1
        $calendar   = IntlDateFormatter::GREGORIAN;
159 1
        $pattern    = \is_string($options['format']) ? $options['format'] : null;
160
161 1
        if (self::HTML5_FORMAT === $pattern) {
162 1
            $builder->addViewTransformer(new DateTimeToRfc3339Transformer(
163 1
                $options['model_timezone'],
164 1
                $options['view_timezone']
165
            ));
166
        } else {
167 1
            $builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
168 1
                $options['model_timezone'],
169 1
                $options['view_timezone'],
170 1
                $dateFormat,
171 1
                $timeFormat,
172 1
                $calendar,
173 1
                $pattern
174
            ));
175
        }
176 1
    }
177
178
    /**
179
     * @param int $format
180
     *
181
     * @throws InvalidOptionsException
182
     */
183 2
    private function checkDateFormat(int $format)
184
    {
185 2
        if (!\in_array($format, self::$acceptedFormats, true)) {
186 1
            throw new InvalidOptionsException(
187 1
                'The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT)
188
                 or a string representing a custom format.'
189
            );
190
        }
191 1
    }
192
193
    /**
194
     * @param FormBuilderInterface $builder
195
     * @param array                $options
196
     *
197
     * @throws UnexpectedTypeException
198
     */
199 1
    private function addModelTransformer(FormBuilderInterface $builder, array $options)
200
    {
201 1
        if ($options['input'] === 'array') {
202 1
            $builder->addModelTransformer(new ReversedTransformer(
203 1
                new CarbonToArrayTransformer($options['model_timezone'], $options['view_timezone'], $this->parts)
204
            ));
205
        } else {
206 1
            $builder->addModelTransformer(new CarbonToDateTimeTransformer());
207
        }
208 1
    }
209
210
    /**
211
     * @param FormView      $view
212
     * @param FormInterface $form
213
     * @param array         $options
214
     */
215 1
    public function buildView(FormView $view, FormInterface $form, array $options): void
216
    {
217 1
        $view->vars['widget'] = $options['widget'];
218
219 1
        if ($options['html5'] && $options['widget'] === 'single_text' && self::HTML5_FORMAT === $options['format']) {
220 1
            $view->vars['type'] = 'datetime-local';
221
        }
222 1
    }
223
}
224