Passed
Push — master ( 355586...94527c )
by Vincent
04:50
created

defaultTransformerExceptionConstraintOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Bdf\Form\Leaf\Date;
4
5
use Bdf\Form\AbstractElementBuilder;
6
use Bdf\Form\Aggregate\FormBuilderInterface;
7
use Bdf\Form\Choice\ChoiceBuilderTrait;
8
use Bdf\Form\Constraint\GreaterThanField;
9
use Bdf\Form\Constraint\GreaterThanOrEqualField;
10
use Bdf\Form\Constraint\LessThanField;
11
use Bdf\Form\Constraint\LessThanOrEqualField;
12
use Bdf\Form\ElementInterface;
13
use Bdf\Form\Transformer\TransformerInterface;
14
use Bdf\Form\Util\FieldPath;
15
use Bdf\Form\Validator\ValueValidatorInterface;
16
use DateTime;
17
use DateTimeImmutable;
18
use DateTimeInterface;
19
use DateTimeZone;
20
use Symfony\Component\Validator\Constraints\GreaterThan;
21
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
22
use Symfony\Component\Validator\Constraints\LessThan;
23
use Symfony\Component\Validator\Constraints\LessThanOrEqual;
24
25
/**
26
 * Builder for @see DateTimeElement
27
 *
28
 * Usage:
29
 * <code>
30
 * $builder
31
 *     ->immutable() // Use DateTimeImmutable type
32
 *     ->format('d/m/Y H:i') // Define the format
33
 *     ->timezone('Europe/Paris') // Date is on Europe/Paris timezone
34
 *     ->after(new DateTime()) // Must be in the future
35
 * ;
36
 * </code>
37
 *
38
 * @see DateTimeElement
39
 * @see FormBuilderInterface::dateTime()
40
 *
41
 * @extends AbstractElementBuilder<DateTimeElement>
42
 */
43
class DateTimeElementBuilder extends AbstractElementBuilder
44
{
45
    use ChoiceBuilderTrait;
46
47
    /**
48
     * @var class-string<DateTimeInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<DateTimeInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<DateTimeInterface>.
Loading history...
49
     */
50
    private $dateTimeClassName = DateTime::class;
51
52
    /**
53
     * @var string
54
     */
55
    private $dateFormat = DateTime::ATOM;
56
57
    /**
58
     * @var DateTimeZone|null
59
     */
60
    private $timezone;
61
62
    /**
63
     * Reset the fields value which are not provided by the format
64
     *
65
     * @var bool
66
     */
67
    private $resetNotProvidedFields = true;
68
69
    /**
70
     * Define the date time class name to use
71
     *
72
     * <code>
73
     * $builder->dateTime('eventDate')->className(Carbon::class); // Use Carbon date time
74
     * </code>
75
     *
76
     * @param class-string<DateTimeInterface> $dateTimeClassName The class name. Must be an implementation of DateTimeInterface
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<DateTimeInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<DateTimeInterface>.
Loading history...
77
     *
78
     * @return $this
79
     *
80
     * @see DateTimeElementBuilder::immutable() For use DateTimeImmutable
81
     */
82 3
    public function className(string $dateTimeClassName): self
83
    {
84 3
        $this->dateTimeClassName = $dateTimeClassName;
85
86 3
        return $this;
87
    }
88
89
    /**
90
     * Use the DateTimeImmutable implementation
91
     * This method is same as calling `$builder->className(DateTimeImmutable::class)`
92
     *
93
     * @return $this
94
     *
95
     * @see DateTimeImmutable
96
     * @see DateTimeElementBuilder::className()
97
     */
98 2
    public function immutable(): self
99
    {
100 2
        return $this->className(DateTimeImmutable::class);
101
    }
102
103
    /**
104
     * Define the date format to use
105
     * Can be one of the `DateTimeInterface` constants
106
     *
107
     * <code>
108
     * $builder->dateTime('birthDate')->format('d/m/Y H:i');
109
     * </code>
110
     *
111
     * @param string $format The format to use
112
     *
113
     * @return $this
114
     *
115
     * @see https://www.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters For the format
116
     */
117 3
    public function format(string $format): self
118
    {
119 3
        $this->dateFormat = $format;
120
121 3
        return $this;
122
    }
123
124
    /**
125
     * Define the used timezone
126
     * - If the timezone is included in the format, the date will be converted to the given timezone (and not the input one)
127
     * - Else, the timezone will be used for parse the date
128
     *
129
     * <code>
130
     * $builder->dateTime('eventDate')->timezone('Europe/Paris');
131
     * $builder->dateTime('eventDate')->timezone(new DateTimeZone('+0200');
132
     * </code>
133
     *
134
     * @param string|DateTimeZone|null $timezone The timezone. If string is given, a new DateTimeZone will be created.
135
     *
136
     * @return $this
137
     */
138 1
    public function timezone($timezone): self
139
    {
140 1
        if (is_string($timezone)) {
141 1
            $timezone = new DateTimeZone($timezone);
142
        }
143
144 1
        $this->timezone = $timezone;
145
146 1
        return $this;
147
    }
148
149
    /**
150
     * Define that the element date must be before the given date
151
     *
152
     * <code>
153
     * $builder->dateTime('birthDate')->before(new DateTime(), 'The date must be in the past');
154
     * </code>
155
     *
156
     * @param DateTimeInterface $dateTime Date to compare
157
     * @param string|null $message The error message
158
     * @param bool $orEqual Does the element date can be equal ?
159
     *
160
     * @return $this
161
     */
162 3
    public function before(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): self
163
    {
164 3
        $constraint = $orEqual ? new LessThanOrEqual($dateTime) : new LessThan($dateTime);
165
166 3
        if ($message) {
167 2
            $constraint->message = $message;
168
        }
169
170 3
        return $this->satisfy($constraint);
171
    }
172
173
    /**
174
     * Define that the element date must be before the date of the other field
175
     *
176
     * Note: The current field must depends of the comparison field.
177
     *
178
     * <code>
179
     * $builder
180
     *     ->dateTime('dateStart')
181
     *     ->depends('dateEnd')
182
     *     ->beforeField('dateEnd')
183
     * ;
184
     * </code>
185
     *
186
     * @param string $field Other field to compare with. This is a field path
187
     * @param string|null $message The error message
188
     * @param bool $orEqual Does the element date can be equal ?
189
     *
190
     * @return $this
191
     *
192
     * @see FieldPath::parse() For the field path syntax
193
     */
194 2
    public function beforeField(string $field, ?string $message = null, bool $orEqual = false): self
195
    {
196 2
        $constraint = $orEqual ? new LessThanOrEqualField($field) : new LessThanField($field);
197
198 2
        if ($message) {
199 2
            $constraint->message = $message;
200
        }
201
202 2
        return $this->satisfy($constraint);
203
    }
204
205
    /**
206
     * Define that the element date must be after the given date
207
     *
208
     * <code>
209
     * $builder->dateTime('eventDate')->after(new DateTime(), 'The date must be in the future', true);
210
     * </code>
211
     *
212
     * @param DateTimeInterface $dateTime Date to compare
213
     * @param string|null $message The error message
214
     * @param bool $orEqual Does the element date can be equal ?
215
     *
216
     * @return $this
217
     */
218 4
    public function after(DateTimeInterface $dateTime, ?string $message = null, bool $orEqual = false): self
219
    {
220 4
        $constraint = $orEqual ? new GreaterThanOrEqual($dateTime) : new GreaterThan($dateTime);
221
222 4
        if ($message) {
223 2
            $constraint->message = $message;
224
        }
225
226 4
        return $this->satisfy($constraint);
227
    }
228
229
    /**
230
     * Define that the element date must be after the date of the other field
231
     *
232
     * Note: The current field must depends of the comparison field.
233
     *
234
     * <code>
235
     * $builder
236
     *     ->dateTime('dateEnd')
237
     *     ->depends('dateStart')
238
     *     ->afterField('dateStart')
239
     * ;
240
     * </code>
241
     *
242
     * @param string $field Other field to compare with. This is a field path
243
     * @param string|null $message The error message
244
     * @param bool $orEqual Does the element date can be equal ?
245
     *
246
     * @return $this
247
     *
248
     * @see FieldPath::parse() For the field path syntax
249
     */
250 2
    public function afterField(string $field, ?string $message = null, bool $orEqual = false): self
251
    {
252 2
        $constraint = $orEqual ? new GreaterThanOrEqualField($field) : new GreaterThanField($field);
253
254 2
        if ($message) {
255 2
            $constraint->message = $message;
256
        }
257
258 2
        return $this->satisfy($constraint);
259
    }
260
261
    /**
262
     * Does the fields which are not provided by the format will be reset (and set to UNIX time)
263
     * When enabled, the character "|" will be added at the end of the format (see: https://www.php.net/datetime.createfromformat)
264
     *
265
     * Note: By default this option is enabled
266
     *
267
     * <code>
268
     * // Reset fields (by default)
269
     * $builder->dateTime('day')->resetNotProvidedFields(true)->format('Y-m-d');
270
     * $form = $builder->buildElement();
271
     * $form->submit(['day' => '2020-10-21']);
272
     * $form['day']->element()->value(); // => new DateTime('2020-10-21 00:00:00'); // Time set to 0
273
     *
274
     * // Do not reset fields
275
     * $builder->dateTime('day')->resetNotProvidedFields(false)->format('Y-m-d');
276
     * $form = $builder->buildElement();
277
     * $form->submit(['day' => '2020-10-21']);
278
     * $form['day']->element()->value(); // => new DateTime('2020-10-21 15:25:36'); // Use current time
279
     * </code>
280
     *
281
     * @param bool $flag Reset or not the fields
282
     *
283
     * @return $this
284
     */
285 1
    public function resetNotProvidedFields(bool $flag = true): self
286
    {
287 1
        $this->resetNotProvidedFields = $flag;
288
289 1
        return $this;
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 31
    protected function defaultTransformerExceptionConstraintOptions(): array
296
    {
297
        return [
298 31
            'message' => 'This value is not a valid datetime.',
299
            'code' => 'INVALID_DATETIME_ERROR',
300
        ];
301
    }
302
303
    /**
304
     * {@inheritdoc}
305
     *
306
     * @return DateTimeElement
307
     */
308 31
    protected function createElement(ValueValidatorInterface $validator, TransformerInterface $transformer): ElementInterface
309
    {
310 31
        return new DateTimeElement(
311 31
            $validator,
312 31
            $transformer,
313 31
            $this->getChoices(),
314 31
            $this->dateTimeClassName,
315 31
            $this->dateFormat,
316 31
            $this->timezone,
317 31
            $this->resetNotProvidedFields
318
        );
319
    }
320
}
321