Passed
Push — main ( 5d7b01...0a20e8 )
by Thierry
02:19
created

TableAdmin::getTableFields()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
c 0
b 0
f 0
nc 7
nop 1
dl 0
loc 34
rs 9.2728
1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
7
8
use Exception;
9
10
use function compact;
11
use function preg_match;
12
use function ksort;
13
use function array_key_exists;
14
use function array_map;
15
use function implode;
16
17
18
/**
19
 * Admin table functions
20
 */
21
class TableAdmin extends AbstractAdmin
22
{
23
    use Traits\TableTrait;
1 ignored issue
show
introduced by
The trait Lagdo\DbAdmin\DbAdmin\Traits\TableTrait requires some properties which are not provided by Lagdo\DbAdmin\DbAdmin\TableAdmin: $fullType, $name, $null, $fieldName, $default, $type
Loading history...
24
25
    /**
26
     * @var string
27
     */
28
    private $after = '';
29
30
    /**
31
     * @var array
32
     */
33
    private $fields = [];
34
35
    /**
36
     * @var TableEntity
37
     */
38
    private $attrs;
39
40
    /**
41
     * Get details about a table
42
     *
43
     * @param string $table     The table name
44
     *
45
     * @return array
46
     */
47
    public function getTableInfo(string $table): array
48
    {
49
        $mainActions = [
50
            'edit-table' => $this->trans->lang('Alter table'),
51
            'drop-table' => $this->trans->lang('Drop table'),
52
            'select-table' => $this->trans->lang('Select'),
53
            'insert-table' => $this->trans->lang('New item'),
54
        ];
55
56
        // From table.inc.php
57
        $status = $this->status($table);
58
        $name = $this->util->tableName($status);
59
        $title = $this->trans->lang('Table') . ': ' . ($name != '' ? $name : $this->util->html($table));
60
61
        $comment = $status->comment;
62
63
        $tabs = [
64
            'fields' => $this->trans->lang('Columns'),
65
            // 'indexes' => $this->trans->lang('Indexes'),
66
            // 'foreign-keys' => $this->trans->lang('Foreign keys'),
67
            // 'triggers' => $this->trans->lang('Triggers'),
68
        ];
69
        if ($this->driver->isView($status)) {
70
            if ($this->driver->support('view_trigger')) {
71
                $tabs['triggers'] = $this->trans->lang('Triggers');
72
            }
73
        } else {
74
            if ($this->driver->support('indexes')) {
75
                $tabs['indexes'] = $this->trans->lang('Indexes');
76
            }
77
            if ($this->driver->supportForeignKeys($status)) {
78
                $tabs['foreign-keys'] = $this->trans->lang('Foreign keys');
79
            }
80
            if ($this->driver->support('trigger')) {
81
                $tabs['triggers'] = $this->trans->lang('Triggers');
82
            }
83
        }
84
85
        return compact('mainActions', 'title', 'comment', 'tabs');
86
    }
87
88
    /**
89
     * Get the fields of a table
90
     *
91
     * @param string $table The table name
92
     *
93
     * @return array
94
     * @throws Exception
95
     */
96
    public function getTableFields(string $table): array
97
    {
98
        // From table.inc.php
99
        $fields = $this->driver->fields($table);
100
        if (empty($fields)) {
101
            throw new Exception($this->driver->error());
102
        }
103
104
        $mainActions = $this->getTableLinks();
105
106
        $headers = [
107
            $this->trans->lang('Name'),
108
            $this->trans->lang('Type'),
109
            $this->trans->lang('Collation'),
110
        ];
111
        $hasComment = $this->driver->support('comment');
112
        if ($hasComment) {
113
            $headers[] = $this->trans->lang('Comment');
114
        }
115
116
        $details = [];
117
        foreach ($fields as $field) {
118
            $detail = [
119
                'name' => $this->util->html($field->name),
120
                'type' => $this->getFieldType($field),
121
                'collation' => $this->util->html($field->collation),
122
            ];
123
            if ($hasComment) {
124
                $detail['comment'] = $this->util->html($field->comment);
125
            }
126
            $details[] = $detail;
127
        }
128
129
        return compact('mainActions', 'headers', 'details');
130
    }
131
132
    /**
133
     * Get the indexes of a table
134
     *
135
     * @param string $table     The table name
136
     *
137
     * @return array|null
138
     */
139
    public function getTableIndexes(string $table)
140
    {
141
        if (!$this->driver->support('indexes')) {
142
            return null;
143
        }
144
145
        // From table.inc.php
146
        $indexes = $this->driver->indexes($table);
147
        $mainActions = [
148
            'create' => $this->trans->lang('Alter indexes'),
149
        ];
150
151
        $headers = [
152
            $this->trans->lang('Name'),
153
            $this->trans->lang('Type'),
154
            $this->trans->lang('Column'),
155
        ];
156
157
        $details = [];
158
        // From adminer.inc.php
159
        foreach ($indexes as $name => $index) {
160
            ksort($index->columns); // enforce correct columns order
161
            $print = [];
162
            foreach ($index->columns as $key => $val) {
163
                $value = '<i>' . $this->util->html($val) . '</i>';
164
                if (array_key_exists($key, $index->lengths)) {
165
                    $value .= '(' . $index->lengths[$key] . ')';
166
                }
167
                if (array_key_exists($key, $index->descs)) {
168
                    $value .= ' DESC';
169
                }
170
                $print[] = $value;
171
            }
172
            $details[] = [
173
                'name' => $this->util->html($name),
174
                'type' => $index->type,
175
                'desc' => implode(', ', $print),
176
            ];
177
        }
178
179
        return compact('mainActions', 'headers', 'details');
180
    }
181
182
    /**
183
     * Get the foreign keys of a table
184
     *
185
     * @param string $table     The table name
186
     *
187
     * @return array|null
188
     */
189
    public function getTableForeignKeys(string $table)
190
    {
191
        $status = $this->status($table);
192
        if (!$this->driver->supportForeignKeys($status)) {
193
            return null;
194
        }
195
196
        // From table.inc.php
197
        $mainActions = [
198
            $this->trans->lang('Add foreign key'),
199
        ];
200
201
        $headers = [
202
            $this->trans->lang('Name'),
203
            $this->trans->lang('Source'),
204
            $this->trans->lang('Target'),
205
            $this->trans->lang('ON DELETE'),
206
            $this->trans->lang('ON UPDATE'),
207
        ];
208
209
        $foreignKeys = $this->driver->foreignKeys($table);
210
        $details = [];
211
        // From table.inc.php
212
        foreach ($foreignKeys as $name => $foreignKey) {
213
            $target = '';
214
            if ($foreignKey->database != '') {
215
                $target .= '<b>' . $this->util->html($foreignKey->database) . '</b>.';
216
            }
217
            if ($foreignKey->schema != '') {
218
                $target .= '<b>' . $this->util->html($foreignKey->schema) . '</b>.';
219
            }
220
            $target = $this->util->html($foreignKey->table) .
221
                '(' . implode(', ', array_map(function ($key) {
222
                    return $this->util->html($key);
223
                }, $foreignKey->target)) . ')';
224
            $details[] = [
225
                'name' => $this->util->html($name),
226
                'source' => '<i>' . implode(
227
                    '</i>, <i>',
228
                    array_map(function ($key) {
229
                        return $this->util->html($key);
230
                    }, $foreignKey->source)
231
                ) . '</i>',
232
                'target' => $target,
233
                'onDelete' => $this->util->html($foreignKey->onDelete),
234
                'onUpdate' => $this->util->html($foreignKey->onUpdate),
235
            ];
236
        }
237
238
        return compact('mainActions', 'headers', 'details');
239
    }
240
241
    /**
242
     * Get the triggers of a table
243
     *
244
     * @param string $table     The table name
245
     *
246
     * @return array|null
247
     */
248
    public function getTableTriggers(string $table)
249
    {
250
        if (!$this->driver->support('trigger')) {
251
            return null;
252
        }
253
254
        $mainActions = [
255
            $this->trans->lang('Add trigger'),
256
        ];
257
258
        $headers = [
259
            $this->trans->lang('Name'),
260
            '&nbsp;',
261
            '&nbsp;',
262
            '&nbsp;',
263
        ];
264
265
        $details = [];
266
        // From table.inc.php
267
        $triggers = $this->driver->triggers($table);
268
        foreach ($triggers as $name => $trigger) {
269
            $details[] = [
270
                $this->util->html($trigger->timing),
271
                $this->util->html($trigger->event),
272
                $this->util->html($name),
273
                $this->trans->lang('Alter'),
274
            ];
275
        }
276
277
        return compact('mainActions', 'headers', 'details');
278
    }
279
280
    /**
281
     * Get required data for create/update on tables
282
     *
283
     * @param string $table The table name
284
     *
285
     * @return array
286
     * @throws Exception
287
     */
288
    public function getTableData(string $table = ''): array
289
    {
290
        $mainActions = [
291
            'table-save' => $this->trans->lang('Save'),
292
            'table-cancel' => $this->trans->lang('Cancel'),
293
        ];
294
295
        // From create.inc.php
296
        $status = [];
297
        $fields = [];
298
        if ($table !== '') {
299
            $status = $this->driver->tableStatus($table);
300
            if (!$status) {
301
                throw new Exception($this->trans->lang('No tables.'));
302
            }
303
            $fields = $this->driver->fields($table);
304
        }
305
306
        $this->getForeignKeys($table);
307
308
        $hasAutoIncrement = false;
309
        foreach ($fields as &$field) {
310
            $hasAutoIncrement = $hasAutoIncrement || $field->autoIncrement;
311
            $field->hasDefault = $field->default !== null;
312
            if (preg_match('~^CURRENT_TIMESTAMP~i', $field->onUpdate)) {
313
                $field->onUpdate = 'CURRENT_TIMESTAMP';
314
            }
315
316
            $type = $field->type;
317
            $field->types = $this->getFieldTypes($type);
318
            $field->lengthRequired = !$field->length && preg_match('~var(char|binary)$~', $type);
319
            $field->collationHidden = !preg_match('~(char|text|enum|set)$~', $type);
320
            $field->unsignedHidden = !(!$type || preg_match($this->driver->numberRegex(), $type));
321
            $field->onUpdateHidden = !preg_match('~timestamp|datetime~', $type);
322
            $field->onDeleteHidden = !preg_match('~`~', $type);
323
        }
324
        $options = [
325
            'hasAutoIncrement' => $hasAutoIncrement,
326
            'onUpdate' => ['CURRENT_TIMESTAMP'],
327
            'onDelete' => $this->driver->onActions(),
328
        ];
329
330
        $collations = $this->driver->collations();
331
        $engines = $this->driver->engines();
332
        $support = [
333
            'columns' => $this->driver->support('columns'),
334
            'comment' => $this->driver->support('comment'),
335
            'partitioning' => $this->driver->support('partitioning'),
336
            'move_col' => $this->driver->support('move_col'),
337
            'drop_col' => $this->driver->support('drop_col'),
338
        ];
339
340
        $foreignKeys = $this->foreignKeys;
341
        $unsigned = $this->driver->unsigned();
342
        // Give the var a better name
343
        $table = $status;
344
        return compact('mainActions', 'table', 'foreignKeys', 'fields',
345
            'options', 'collations', 'engines', 'support', 'unsigned');
346
    }
347
348
    /**
349
     * Get fields for a new column
350
     *
351
     * @return TableFieldEntity
352
     */
353
    public function getTableField(): TableFieldEntity
354
    {
355
        $this->getForeignKeys();
356
        $field = new TableFieldEntity();
357
        $field->types = $this->getFieldTypes();
358
        return $field;
359
    }
360
361
    /**
362
     * Create or alter a table
363
     *
364
     * @param array  $values    The table values
365
     * @param string $table     The table name
366
     *
367
     * @return void
368
     */
369
    private function makeTableAttrs(array $values, string $table = '')
370
    {
371
        // From create.inc.php
372
        $values['fields'] = (array)$values['fields'];
373
        if ($values['autoIncrementCol']) {
374
            $values['fields'][$values['autoIncrementCol']]['autoIncrement'] = true;
375
        }
376
377
        $this->attrs = new TableEntity(\trim($values['name']));
378
        $this->after = ' FIRST';
379
380
        $this->getForeignKeys();
381
382
        $this->fields = ($table !== '' ? $this->driver->fields($table) : []);
383
        foreach ($values['fields'] as $key => $field) {
384
            $fieldName = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $fieldName is dead and can be removed.
Loading history...
385
            $fieldName = $field['orig'];
386
            $field = TableFieldEntity::make($field);
387
            // Originally, deleted fields have the "field" field set to an empty string.
388
            // But in our implementation, the "name" field is not set.
389
            if ($field->name != '') {
390
                $field->autoIncrement = ($key == $values['autoIncrementCol']);
391
                $this->addFieldToAttrs($field, $table);
392
                $this->after = ' AFTER ' . $this->driver->escapeId($field->name);
393
            } elseif ($fieldName !== '') {
394
                // A missing "name" field and a not empty "orig" field means the column is to be dropped.
395
                $this->attrs->dropped[] = $fieldName;
396
            }
397
            if ($fieldName !== '') {
398
                $this->after = '';
399
            }
400
        }
401
402
        // For now, partitioning is not implemented
403
        // $this->setPartitionAttr();
404
405
        $this->setValueAttrs($values);
406
    }
407
408
    /**
409
     * Create a table
410
     *
411
     * @param array  $values    The table values
412
     *
413
     * @return array
414
     */
415
    public function createTable(array $values): array
416
    {
417
        $this->makeTableAttrs($values);
418
        $success = $this->driver->createTable($this->attrs);
419
        $error = $this->driver->error();
420
        $message = $this->trans->lang('Table has been created.');
421
422
        return compact('success', 'error', 'message');
423
    }
424
425
    /**
426
     * Alter a table
427
     *
428
     * @param string $table The table name
429
     * @param array $values The table values
430
     *
431
     * @return array
432
     * @throws Exception
433
     */
434
    public function alterTable(string $table, array $values): array
435
    {
436
        $this->tableStatus = $this->driver->tableStatus($table);
437
        if (!$this->tableStatus) {
438
            throw new Exception($this->trans->lang('No tables.'));
439
        }
440
441
        $this->makeTableAttrs($values, $table);
442
        $success = $this->driver->alterTable($table, $this->attrs);
443
        $error = $this->driver->error();
444
        $message = $this->trans->lang('Table has been altered.');
445
446
        return compact('success', 'error', 'message');
447
    }
448
449
    /**
450
     * Drop a table
451
     *
452
     * @param string $table     The table name
453
     *
454
     * @return array
455
     */
456
    public function dropTable(string $table): array
457
    {
458
        $success = $this->driver->dropTables([$table]);
459
460
        $error = $this->driver->error();
461
462
        $message = $this->trans->lang('Table has been dropped.');
463
464
        return compact('success', 'message', 'error');
465
    }
466
}
467