Passed
Push — main ( 20361b...6d1b9c )
by Thierry
06:44 queued 04:24
created

DataFieldInput::getJsonFieldInput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
nc 1
nop 2
dl 0
loc 11
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 in_array;
11
use function is_array;
12
use function is_string;
13
use function min;
14
use function preg_match;
15
use function preg_match_all;
16
use function reset;
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 $fieldName
42
     * @param string|null $enumValue
43
     *
44
     * @return array
45
     */
46
    private function getCheckedAttr(FieldEditEntity $editField, string $fieldName, string|null $enumValue): array
47
    {
48
        $checked = is_array($editField->value) ?
49
            in_array($fieldName, $editField->value) : $editField->value === $enumValue;
50
        return $checked ? ['checked' => 'checked'] : [];
51
    }
52
53
    /**
54
     * Get data for enum or set input field
55
     * 
56
     * @param FieldEditEntity $editField
57
     */
58
    private function itemList(FieldEditEntity $editField, array $attrs, string $default = ""): array|null
59
    {
60
        // From html.inc.php: function enum_input(string $type, string $attrs, array $field, $value, string $empty = "")
61
        $inputType = match($editField->type) {
62
            'enum' => 'radio',
63
            'set' => 'checkbox',
64
            default => null,
65
        };
66
        if ($inputType === null) {
67
            // Only for enums and sets
68
            return null;
69
        }
70
71
        $fieldId = $attrs['id'];
72
        $prefix = $editField->type === 'enum' ? 'val-' : '';
73
        $items = [];
74
        if ($editField->field->null && $prefix) {
75
            $checkedAttr = $this->getCheckedAttr($editField, 'null', null);
76
            $items[] = [
77
                'attrs' => [
78
                    'type' => $inputType,
79
                    ...$attrs,
80
                    'id' => "{$fieldId}_null",
81
                    'value' => 'null',
82
                    ...$checkedAttr,
83
                ],
84
                'label' => "<i>$default</i>",
85
            ];
86
        }
87
88
        // The length value to consider depends on the field type.
89
        preg_match_all("~'((?:[^']|'')*)'~", $editField->field->length, $matches);
90
        foreach (($matches[1] ?? []) as $enumValue) {
91
            $enumValue = stripcslashes(str_replace("''", "'", $enumValue));
92
            $fieldName = "$prefix$enumValue";
93
            $checkedAttr = $this->getCheckedAttr($editField, $fieldName, $enumValue);
94
            $items[] = [
95
                'attrs' => [
96
                    'type' => $inputType,
97
                    ...$attrs,
98
                    'id' => "{$fieldId}_{$enumValue}",
99
                    'value' => $this->utils->html($fieldName),
100
                    ...$checkedAttr,
101
                ],
102
                'label' => $this->utils->html($enumValue),
103
            ];
104
        }
105
106
        return $items;
107
    }
108
109
    /**
110
     * @param FieldEditEntity $editField
111
     * @param array $attrs
112
     *
113
     * @return array
114
     */
115
    private function getEnumFieldInput(FieldEditEntity $editField, array $attrs): array
116
    {
117
        // From adminer.inc.php: function editInput(?string $table, array $field, string $attrs, $value): string
118
        $values = [
119
            'type' => 'enum',
120
            'items' => $this->itemList($editField, $attrs, 'NULL'),
121
        ];
122
123
        if ($this->action === 'select') {
124
            $values['orig'] = [
125
                'attrs' => [
126
                    'type' => 'radio',
127
                    ...$attrs,
128
                    'value' => 'orig',
129
                    'checked' => 'checked',
130
                ],
131
                'label' => '<i>' . $this->utils->trans->lang('original') . '</i>',
132
            ];
133
        }
134
135
        return $values;
136
    }
137
138
    /**
139
     * @param FieldEditEntity $editField
140
     * @param array $attrs
141
     *
142
     * @return array
143
     */
144
    private function getSetFieldInput(FieldEditEntity $editField, array $attrs): array
145
    {
146
        if (is_string($editField->value)) {
147
            $editField->value = explode(",", $editField->value);
148
        }
149
150
        return [
151
            'type' => 'set',
152
            'items' => $this->itemList($editField, $attrs),
153
        ];
154
    }
155
156
    /**
157
     * @param FieldEditEntity $editField
158
     * @param array $attrs
159
     *
160
     * @return array
161
     */
162
    private function getBoolFieldInput(FieldEditEntity $editField, array $attrs): array
163
    {
164
        $checkedAttr = $editField->isChecked() ? ['checked' => 'checked'] : [];
165
        return [
166
            'type' => 'bool',
167
            'hidden' => [
168
                'attrs' => [
169
                    'type' => 'hidden',
170
                    ...$attrs,
171
                    'value' => '0',
172
                    'id' => '', // Unset the if value in the $attrs array
173
                ],
174
            ],
175
            'checkbox' => [
176
                'attrs' => [
177
                    'type' => 'checkbox',
178
                    ...$attrs,
179
                    'value' => '1',
180
                    ...$checkedAttr,
181
                ],
182
            ],
183
        ];
184
    }
185
186
    /**
187
     * @param FieldEditEntity $editField
188
     *
189
     * @return bool
190
     */
191
    private function isBlob(FieldEditEntity $editField): bool
192
    {
193
        return $this->utils->isBlob($editField->field) && $this->utils->iniBool("file_uploads");
0 ignored issues
show
Bug introduced by
The method isBlob() does not exist on Lagdo\DbAdmin\Driver\Utils\Utils. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

193
        return $this->utils->/** @scrutinizer ignore-call */ isBlob($editField->field) && $this->utils->iniBool("file_uploads");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method iniBool() does not exist on Lagdo\DbAdmin\Driver\Utils\Utils. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

193
        return $this->utils->isBlob($editField->field) && $this->utils->/** @scrutinizer ignore-call */ iniBool("file_uploads");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
194
    }
195
196
    /**
197
     * @param FieldEditEntity $editField
198
     * @param array $attrs
199
     *
200
     * @return array
201
     */
202
    private function getFileFieldInput(FieldEditEntity $editField, array $attrs): array
203
    {
204
        return [
205
            'type' => 'file',
206
            'attrs' => [
207
                'type' => 'file',
208
                'id' => $attrs['id'],
209
                'name' => "fields-{$editField->name}",
210
            ],
211
        ];
212
    }
213
214
    /**
215
     * @param FieldEditEntity $editField
216
     * @param array $attrs
217
     *
218
     * @return array
219
     */
220
    private function getJsonFieldInput(FieldEditEntity $editField, array $attrs): array
221
    {
222
        return [
223
            'type' => 'json',
224
            'attrs' => [
225
                ...$attrs,
226
                'cols' => '50',
227
                'rows' => '5',
228
                'class' => 'jush-js',
229
            ],
230
            'value' => $this->utils->str->html($editField->value),
231
        ];
232
    }
233
234
    /**
235
     * @param FieldEditEntity $editField
236
     * @param array $attrs
237
     * @param mixed $value
238
     *
239
     * @return array
240
     */
241
    private function getTextFieldInput(FieldEditEntity $editField, array $attrs, bool $isText): array
242
    {
243
        $fieldAttrs = $isText && $this->driver->jush() !== 'sqlite' ? [
244
            'cols' => '50',
245
            'rows' => '5',
246
        ] : [
247
            'cols' => '30',
248
            'rows' => min(5, substr_count($editField->value, "\n") + 1),
249
        ];
250
        return [
251
            'type' => 'text',
252
            'attrs' => [
253
                ...$attrs,
254
                ...$fieldAttrs,
255
            ],
256
            'value' => $this->utils->str->html($editField->value),
257
        ];
258
    }
259
260
    /**
261
     * @param FieldEditEntity $editField
262
     *
263
     * @return int
264
     */
265
    private function getInputFieldMaxLength(FieldEditEntity $editField): int
266
    {
267
        $unsigned = $editField->field->unsigned;
268
        $length = $editField->field->length;
269
        $type = $editField->type;
270
        $types = $this->driver->types();
271
272
        $maxlength = (!preg_match('~int~', $type) &&
273
            preg_match('~^(\d+)(,(\d+))?$~', $length, $match) ?
274
            ((preg_match("~binary~", $type) ? 2 : 1) *
275
                ($match[1] ?? 0) + (($match[3] ?? false) ? 1 : 0) +
276
                (($match[2] ?? false) && !$unsigned ? 1 : 0)) :
277
            ($types[$type] ? $types[$type] + ($unsigned ? 0 : 1) : 0)
278
        );
279
280
        return $this->driver->jush() === 'sql' &&
281
            $this->driver->minVersion(5.6) &&
282
            preg_match('~time~', $type) ?
283
                $maxlength += 7 : // microtime
284
                $maxlength;
285
    }
286
287
    /**
288
     * @param FieldEditEntity $editField
289
     * @param array $attrs
290
     *
291
     * @return array
292
     */
293
    private function getDefaultFieldInput(FieldEditEntity $editField, array $attrs): array
294
    {
295
        $maxlength = $this->getInputFieldMaxLength($editField);
296
        // type='date' and type='time' display localized value which may be confusing,
297
        // type='datetime' uses 'T' as date and time separator
298
299
        if ($editField->isNumber()) {
300
            $attrs['type'] = 'number';
301
        }
302
        $attrs['value'] = $this->utils->html($editField->value ?? '');
303
        if ($maxlength > 0) {
304
            $attrs['data-maxlength'] = $maxlength;
305
        }
306
        if ($editField->bigSize($maxlength)) {
307
            $attrs['size'] = $maxlength > 99 ? '60' : '40';
308
        }
309
310
        return [
311
            'type' => 'input',
312
            'attrs' => $attrs,
313
        ];
314
    }
315
316
    /**
317
     * Get the input field for value
318
     *
319
     * @param FieldEditEntity $editField
320
     * @param bool|null $autofocus
321
     *
322
     * @return array
323
     */
324
    private function getFieldValueInput(FieldEditEntity $editField, bool|null $autofocus): array
325
    {
326
        // From input(array $field, $value, ?string $function, ?bool $autofocus = false) in html.inc.php
327
        $attrs = [
328
            'id' => "fields_{$editField->name}",
329
            'name' => $editField->isEnum() || $editField->isSet() ?
330
                "fields[{$editField->name}][]" : "fields[{$editField->name}]",
331
        ];
332
        if ($editField->isDisabled()) {
333
            $attrs['disabled'] = 'disabled';
334
        }
335
        if ($autofocus) {
336
            $attrs['autofocus'] = true;
337
        }
338
339
        // This function is implemented only for MySQL.
340
        // Todo: check what it actually does.
341
        // echo driver()->unconvertFunction($field) . " ";
342
343
        return match(true) {
344
            $editField->isEnum() => $this->getEnumFieldInput($editField, $attrs),
345
346
            $editField->isBool() => $this->getBoolFieldInput($editField, $attrs),
347
348
            $editField->isSet() => $this->getSetFieldInput($editField, $attrs),
349
350
            $this->isBlob($editField) => $this->getFileFieldInput($editField, $attrs),
351
352
            $editField->isJson() => $this->getJsonFieldInput($editField, $attrs),
353
354
            ($isText = $editField->isText()) || $editField->hasNewLine() =>
355
                $this->getTextFieldInput($editField, $attrs, $isText),
356
357
            default => $this->getDefaultFieldInput($editField, $attrs),
358
        };
359
    }
360
361
    /**
362
     * Get the input field for function
363
     *
364
     * @param FieldEditEntity $editField
365
     *
366
     * @return array|null
367
     */
368
    private function getFieldFunctionInput(FieldEditEntity $editField): array|null
369
    {
370
        // From html.inc.php: function input(array $field, $value, ?string $function, ?bool $autofocus = false)
371
        if ($editField->type === 'enum' || $editField->function === null) {
372
            return null; // No function for enum values
373
        }
374
375
        if (count($editField->functions) <= 1) {
376
            return [
377
                'type' => 'name',
378
                'label' => $this->utils->str->html(reset($editField->functions)),
379
            ];
380
        }
381
382
        $disabledAttr = $editField->isDisabled() ? ['disabled' => 'disabled'] : [];
383
        return [
384
            'type' => 'select',
385
            'attrs' => [
386
                'name' => "function[{$editField->name}]",
387
                ...$disabledAttr,
388
            ],
389
            'options' => $editField->functions,
390
            'value' => $editField->function === null || $editField->hasFunction() ? $editField->function : '',
391
        ];
392
    }
393
394
    /**
395
     * @param FieldEditEntity $editField
396
     * @param bool|null $autofocus
397
     *
398
     * @return void
399
     */
400
    public function setFieldInputValues(FieldEditEntity $editField, bool|null $autofocus): void
401
    {
402
        $editField->functionInput = $this->getFieldFunctionInput($editField);
403
        $editField->valueInput = $this->getFieldValueInput($editField, $autofocus);
404
    }
405
}
406