Passed
Push — main ( 08974e...644444 )
by Thierry
02:28
created

DataFieldInput::setFieldInputValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Page\Dml;
4
5
use Lagdo\DbAdmin\Db\Page\AppPage;
6
use Lagdo\DbAdmin\Driver\DriverInterface;
7
use Lagdo\DbAdmin\Driver\Utils\Utils;
8
9
use function count;
10
use function explode;
11
use function in_array;
12
use function is_array;
13
use function is_string;
14
use function min;
15
use function preg_match;
16
use function preg_match_all;
17
use function stripcslashes;
18
use function str_replace;
19
use function substr_count;
20
21
/**
22
 * Make data for HTML elements in the user forms for data row insert and update.
23
 */
24
class DataFieldInput
25
{
26
    /**
27
     * The constructor
28
     *
29
     * @param AppPage $page
30
     * @param DriverInterface $driver
31
     * @param Utils $utils
32
     * @param string $action
33
     * @param string $operation
34
     */
35
    public function __construct(private AppPage $page, private DriverInterface $driver,
36
        private Utils $utils, private string $action, private string $operation)
37
    {}
38
39
    /**
40
     * @param FieldEditEntity $editField
41
     * @param string $fieldValue
42
     * @param string|null $enumValue
43
     *
44
     * @return bool
45
     */
46
    private function isChecked(FieldEditEntity $editField, string $fieldValue, string|null $enumValue): bool
47
    {
48
        return !is_array($editField->value) ? $editField->value === $enumValue :
49
            in_array($fieldValue, $editField->value);
50
    }
51
52
    /**
53
     * Get data for enum or set input field
54
     * 
55
     * @param FieldEditEntity $editField
56
     */
57
    private function getItemList(FieldEditEntity $editField, array $attrs, string $default = ""): array|null
58
    {
59
        if ($editField->type !== 'enum' && $editField->type !== 'set' ) {
60
            // Only for enums and sets
61
            return null;
62
        }
63
64
        // From html.inc.php: function enum_input(string $type, string $attrs, array $field, $value, string $empty = "")
65
        $prefix = $editField->type === 'enum' ? 'val-' : '';
66
        $items = [];
67
        if ($editField->field->null && $prefix) {
68
            $items[] = [
69
                'attrs' => [
70
                    ...$attrs,
71
                    'id' => "{$attrs['id']}_null", // Overwrite the id value in the $attrs array.
72
                ],
73
                'label' => "<i>$default</i>",
74
                'value' => 'null',
75
                'checked' => $this->isChecked($editField, 'null', null),
76
            ];
77
        }
78
79
        preg_match_all("~'((?:[^']|'')*)'~", $editField->field->length, $matches);
80
        foreach (($matches[1] ?? []) as $enumValue) {
81
            $enumValue = stripcslashes(str_replace("''", "'", $enumValue));
82
            $fieldValue = "$prefix$enumValue";
83
            $items[] = [
84
                'attrs' => [
85
                    ...$attrs,
86
                    'id' => "{$attrs['id']}_{$enumValue}", // Overwrite the id value in the $attrs array.
87
                ],
88
                'label' => $this->utils->html($enumValue),
89
                'value' => $this->utils->html($fieldValue),
90
                'checked' => $this->isChecked($editField, $fieldValue, $enumValue),
91
            ];
92
        }
93
94
        return $items;
95
    }
96
97
    /**
98
     * @param FieldEditEntity $editField
99
     * @param array $attrs
100
     *
101
     * @return array
102
     */
103
    private function getEnumFieldInput(FieldEditEntity $editField, array $attrs): array
104
    {
105
        // From adminer.inc.php: function editInput(?string $table, array $field, string $attrs, $value): string
106
        $items = $this->getItemList($editField, $attrs, 'NULL');
107
        if ($this->action === 'select') {
108
            // Prepend the value to the item list
109
            $items = [[
110
                'attrs' => $attrs,
111
                'label' => '<i>' . $this->utils->trans->lang('original') . '</i>',
112
                'value' => 'orig',
113
                'checked' => true,
114
            ], ...$items];
115
        }
116
117
        return [
118
            'field' => 'enum',
119
            'items' => $items,
120
        ];
121
    }
122
123
    /**
124
     * @param FieldEditEntity $editField
125
     * @param array $attrs
126
     *
127
     * @return array
128
     */
129
    private function getSetFieldInput(FieldEditEntity $editField, array $attrs): array
130
    {
131
        if (is_string($editField->value)) {
132
            $editField->value = explode(",", $editField->value);
133
        }
134
135
        return [
136
            'field' => 'set',
137
            'items' => $this->getItemList($editField, $attrs),
138
        ];
139
    }
140
141
    /**
142
     * @param FieldEditEntity $editField
143
     * @param array $attrs
144
     *
145
     * @return array
146
     */
147
    private function getBoolFieldInput(FieldEditEntity $editField, array $attrs): array
148
    {
149
        return [
150
            'field' => 'bool',
151
            'attrs' => [
152
                'hidden' => [
153
                    ...$attrs,
154
                    'id' => '', // Unset the id value in the $attrs array
155
                    'value' => '0',
156
                ],
157
                'checkbox' => [
158
                    ...$attrs,
159
                    'value' => '1',
160
                ],
161
            ],
162
            'checked' => $editField->isChecked(),
163
        ];
164
    }
165
166
    /**
167
     * @param FieldEditEntity $editField
168
     *
169
     * @return bool
170
     */
171
    private function isBlob(FieldEditEntity $editField): bool
172
    {
173
        return $this->utils->isBlob($editField->field) && $this->utils->iniBool("file_uploads");
174
    }
175
176
    /**
177
     * @param FieldEditEntity $editField
178
     * @param array $attrs
179
     *
180
     * @return array
181
     */
182
    private function getFileFieldInput(FieldEditEntity $editField, array $attrs): array
183
    {
184
        return [
185
            'field' => 'file',
186
            'attrs' => [
187
                'id' => $attrs['id'],
188
                'name' => "fields-{$editField->name}",
189
            ],
190
        ];
191
    }
192
193
    /**
194
     * @param FieldEditEntity $editField
195
     * @param array $attrs
196
     *
197
     * @return array
198
     */
199
    private function getJsonFieldInput(FieldEditEntity $editField, array $attrs): array
200
    {
201
        return [
202
            'field' => 'json',
203
            'attrs' => [
204
                ...$attrs,
205
                'cols' => '50',
206
                'rows' => '5',
207
                'class' => 'jush-js',
208
            ],
209
            'value' => $this->utils->str->html($editField->value ?? ''),
210
        ];
211
    }
212
213
    /**
214
     * @param FieldEditEntity $editField
215
     *
216
     * @return bool
217
     */
218
    private function textSizeIsFixed(FieldEditEntity $editField): bool
219
    {
220
        return ($editField->isText() && $this->driver->jush() !== 'sqlite') || $editField->isSearch();
221
    }
222
223
    /**
224
     * @param FieldEditEntity $editField
225
     * @param array $attrs
226
     *
227
     * @return array
228
     */
229
    private function getTextFieldInput(FieldEditEntity $editField, array $attrs): array
230
    {
231
        $fieldAttrs = $this->textSizeIsFixed($editField) ? [
232
            'cols' => '50',
233
            'rows' => '5',
234
        ] : [
235
            'cols' => '30',
236
            'rows' => min(5, substr_count($editField->value ?? '', "\n") + 1),
237
        ];
238
        return [
239
            'field' => 'text',
240
            'attrs' => [
241
                ...$attrs,
242
                ...$fieldAttrs,
243
            ],
244
            'value' => $this->utils->html($editField->value ?? ''),
245
        ];
246
    }
247
248
    /**
249
     * @param FieldEditEntity $editField
250
     *
251
     * @return int
252
     */
253
    private function getInputFieldMaxLength(FieldEditEntity $editField): int
254
    {
255
        $unsigned = $editField->field->unsigned;
256
        $length = $editField->field->length;
257
        $type = $editField->type;
258
        $types = $this->driver->types();
259
260
        $maxlength = (!preg_match('~int~', $type) &&
261
            preg_match('~^(\d+)(,(\d+))?$~', $length, $match) ?
262
            ((preg_match("~binary~", $type) ? 2 : 1) *
263
                ($match[1] ?? 0) + (($match[3] ?? false) ? 1 : 0) +
264
                (($match[2] ?? false) && !$unsigned ? 1 : 0)) :
265
            (isset($types[$type]) ? $types[$type] + ($unsigned ? 0 : 1) : 0)
266
        );
267
268
        return $this->driver->jush() === 'sql' &&
269
            $this->driver->minVersion(5.6) &&
270
            preg_match('~time~', $type) ?
271
                $maxlength += 7 : // microtime
272
                $maxlength;
273
    }
274
275
    /**
276
     * @param FieldEditEntity $editField
277
     * @param array $attrs
278
     *
279
     * @return array
280
     */
281
    private function getDefaultFieldInput(FieldEditEntity $editField, array $attrs): array
282
    {
283
        $maxlength = $this->getInputFieldMaxLength($editField);
284
        // type='date' and type='time' display localized value which may be confusing,
285
        // type='datetime' uses 'T' as date and time separator
286
287
        if ($editField->isNumber()) {
288
            $attrs['type'] = 'number';
289
        }
290
        if ($maxlength > 0) {
291
            $attrs['data-maxlength'] = $maxlength;
292
        }
293
        if ($editField->bigSize($maxlength)) {
294
            $attrs['size'] = $maxlength > 99 ? '60' : '40';
295
        }
296
297
        return [
298
            'field' => 'input',
299
            'attrs' => $attrs,
300
            'value' => $this->utils->html($editField->value ?? ''),
301
        ];
302
    }
303
304
    /**
305
     * Get the input field for value
306
     *
307
     * @param FieldEditEntity $editField
308
     * @param bool|null $autofocus
309
     *
310
     * @return array
311
     */
312
    private function getFieldValueInput(FieldEditEntity $editField, bool|null $autofocus): array
313
    {
314
        // From input(array $field, $value, ?string $function, ?bool $autofocus = false) in html.inc.php
315
        $attrs = [
316
            'id' => "fields_{$editField->name}",
317
            'name' => $editField->isEnum() || $editField->isSet() ?
318
                "field_values[{$editField->name}][]" : "field_values[{$editField->name}]",
319
        ];
320
        if ($editField->isDisabled()) {
321
            $attrs['disabled'] = 'disabled';
322
        }
323
        if ($autofocus) {
324
            $attrs['autofocus'] = true;
325
        }
326
327
        // This function is implemented only for MySQL.
328
        // Todo: check what it actually does.
329
        // echo driver()->unconvertFunction($field) . " ";
330
331
        return match(true) {
332
            $editField->isEnum() => $this->getEnumFieldInput($editField, $attrs),
333
            $editField->isBool() => $this->getBoolFieldInput($editField, $attrs),
334
            $editField->isSet() => $this->getSetFieldInput($editField, $attrs),
335
            $this->isBlob($editField) => $this->getFileFieldInput($editField, $attrs),
336
            $editField->isJson() => $this->getJsonFieldInput($editField, $attrs),
337
            $editField->editText() => $this->getTextFieldInput($editField, $attrs),
338
            default => $this->getDefaultFieldInput($editField, $attrs),
339
        };
340
    }
341
342
    /**
343
     * Get the input field for function
344
     *
345
     * @param FieldEditEntity $editField
346
     *
347
     * @return array|null
348
     */
349
    private function getFieldFunctionInput(FieldEditEntity $editField): array|null
350
    {
351
        // From html.inc.php: function input(array $field, $value, ?string $function, ?bool $autofocus = false)
352
        if ($editField->type === 'enum' || $editField->function === null) {
353
            return null; // No function for enum values
354
        }
355
356
        if (count($editField->functions) < 2) {
357
            return [
358
                'label' => $this->utils->str->html($editField->functions[0] ?? ''),
359
            ];
360
        }
361
362
        $disabledAttr = $editField->isDisabled() ? ['disabled' => 'disabled'] : [];
363
        return [
364
            'select' => [
365
                'attrs' => [
366
                    'name' => "field_functions[{$editField->name}]",
367
                    ...$disabledAttr,
368
                ],
369
                'options' => $editField->functions,
370
                'value' => $editField->functionValue(),
371
            ],
372
        ];
373
    }
374
375
    /**
376
     * @param FieldEditEntity $editField
377
     * @param bool|null $autofocus
378
     *
379
     * @return void
380
     */
381
    public function setFieldInputValues(FieldEditEntity $editField, bool|null $autofocus): void
382
    {
383
        $editField->functionInput = $this->getFieldFunctionInput($editField);
384
        $editField->valueInput = $this->getFieldValueInput($editField, $autofocus);
385
    }
386
}
387