Passed
Push — master ( 360a7e...05dafc )
by William
10:25 queued 12s
created

RelationController::indexAction()   D

Complexity

Conditions 20
Paths 98

Size

Total Lines 108
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 64
nc 98
nop 0
dl 0
loc 108
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Holds the PhpMyAdmin\Controllers\Table\RelationController
4
 *
5
 * @package PhpMyAdmin\Controllers
6
 */
7
declare(strict_types=1);
8
9
namespace PhpMyAdmin\Controllers\Table;
10
11
use PhpMyAdmin\Core;
12
use PhpMyAdmin\DatabaseInterface;
13
use PhpMyAdmin\Html\Generator;
14
use PhpMyAdmin\Index;
15
use PhpMyAdmin\Relation;
16
use PhpMyAdmin\Response;
17
use PhpMyAdmin\Table;
18
use PhpMyAdmin\Template;
19
use PhpMyAdmin\Util;
20
21
/**
22
 * Handles table relation logic
23
 *
24
 * @package PhpMyAdmin\Controllers
25
 */
26
class RelationController extends AbstractController
27
{
28
    /**
29
     * @var array
30
     */
31
    protected $options_array;
32
33
    /**
34
     * @var array
35
     */
36
    protected $cfgRelation;
37
38
    /**
39
     * @var array
40
     */
41
    protected $existrel;
42
43
    /**
44
     * @var string
45
     */
46
    protected $tbl_storage_engine;
47
48
    /**
49
     * @var array
50
     */
51
    protected $existrel_foreign;
52
53
    /**
54
     * @var Table
55
     */
56
    protected $upd_query;
57
58
    /**
59
     * @var Relation
60
     */
61
    private $relation;
62
63
    /**
64
     * Constructor
65
     *
66
     * @param Response          $response           Response object
67
     * @param DatabaseInterface $dbi                DatabaseInterface object
68
     * @param Template          $template           Template object
69
     * @param string            $db                 Database name
70
     * @param string            $table              Table name
71
     * @param array|null        $options_array      Options
72
     * @param array|null        $cfgRelation        Config relation
73
     * @param string            $tbl_storage_engine Table storage engine
74
     * @param array|null        $existrel           Relations
75
     * @param array|null        $existrel_foreign   External relations
76
     * @param Table             $upd_query          Update query
77
     * @param Relation          $relation           Relation instance
78
     */
79
    public function __construct(
80
        $response,
81
        $dbi,
82
        Template $template,
83
        $db,
84
        $table,
85
        $options_array,
86
        $cfgRelation,
87
        $tbl_storage_engine,
88
        $existrel,
89
        $existrel_foreign,
90
        $upd_query,
91
        Relation $relation
92
    ) {
93
        parent::__construct($response, $dbi, $template, $db, $table);
94
95
        $this->options_array = $options_array;
96
        $this->cfgRelation = $cfgRelation;
97
        $this->tbl_storage_engine = $tbl_storage_engine;
98
        $this->existrel = $existrel;
99
        $this->existrel_foreign = $existrel_foreign;
100
        $this->upd_query = $upd_query;
101
        $this->relation = $relation;
102
    }
103
104
    /**
105
     * Index
106
     *
107
     * @return void
108
     */
109
    public function indexAction()
110
    {
111
        global $route;
112
113
        // Send table of column names to populate corresponding dropdowns depending
114
        // on the current selection
115
        if (isset($_POST['getDropdownValues'])
116
            && $_POST['getDropdownValues'] === 'true'
117
        ) {
118
            // if both db and table are selected
119
            if (isset($_POST['foreignTable'])) {
120
                $this->getDropdownValueForTableAction();
121
            } else { // if only the db is selected
122
                $this->getDropdownValueForDbAction();
123
            }
124
            return;
125
        }
126
127
        $this->response->getHeader()->getScripts()->addFiles(
128
            [
129
                'table/relation.js',
130
                'indexes.js',
131
            ]
132
        );
133
134
        // Set the database
135
        $this->dbi->selectDb($this->db);
136
137
        // updates for Internal relations
138
        if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
139
            $this->updateForInternalRelationAction();
140
        }
141
142
        // updates for foreign keys
143
        $this->updateForForeignKeysAction();
144
145
        // Updates for display field
146
        if ($this->cfgRelation['displaywork'] && isset($_POST['display_field'])) {
147
            $this->updateForDisplayField();
148
        }
149
150
        // If we did an update, refresh our data
151
        if (isset($_POST['destination_db']) && $this->cfgRelation['relwork']) {
152
            $this->existrel = $this->relation->getForeigners(
153
                $this->db,
154
                $this->table,
155
                '',
156
                'internal'
157
            );
158
        }
159
        if (isset($_POST['destination_foreign_db'])
160
            && Util::isForeignKeySupported($this->tbl_storage_engine)
161
        ) {
162
            $this->existrel_foreign = $this->relation->getForeigners(
163
                $this->db,
164
                $this->table,
165
                '',
166
                'foreign'
167
            );
168
        }
169
170
        /**
171
         * Dialog
172
         */
173
        // Now find out the columns of our $table
174
        // need to use DatabaseInterface::QUERY_STORE with $this->dbi->numRows()
175
        // in mysqli
176
        $columns = $this->dbi->getColumns($this->db, $this->table);
177
178
        $column_array = [];
179
        $column_hash_array = [];
180
        $column_array[''] = '';
181
        foreach ($columns as $column) {
182
            if (strtoupper($this->tbl_storage_engine) == 'INNODB'
183
                || ! empty($column['Key'])
184
            ) {
185
                $column_array[$column['Field']] = $column['Field'];
186
                $column_hash_array[$column['Field']] = md5($column['Field']);
187
            }
188
        }
189
        if ($GLOBALS['cfg']['NaturalOrder']) {
190
            uksort($column_array, 'strnatcasecmp');
191
        }
192
193
        // common form
194
        $engine = $this->dbi->getTable($this->db, $this->table)->getStorageEngine();
195
        $foreignKeySupported = Util::isForeignKeySupported($this->tbl_storage_engine);
196
        $this->response->addHTML(
197
            $this->template->render('table/relation/common_form', [
198
                'is_foreign_key_supported' => Util::isForeignKeySupported($engine),
199
                'db' => $this->db,
200
                'table' => $this->table,
201
                'cfg_relation' => $this->cfgRelation,
202
                'tbl_storage_engine' => $this->tbl_storage_engine,
203
                'existrel' => isset($this->existrel) ? $this->existrel : [],
204
                'existrel_foreign' => is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign)
205
                    ? $this->existrel_foreign['foreign_keys_data'] : [],
206
                'options_array' => $this->options_array,
207
                'column_array' => $column_array,
208
                'column_hash_array' => $column_hash_array,
209
                'save_row' => array_values($columns),
210
                'url_params' => $GLOBALS['url_params'],
211
                'databases' => $GLOBALS['dblist']->databases,
212
                'dbi' => $this->dbi,
213
                'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'],
214
                'foreignKeySupported' => $foreignKeySupported,
215
                'displayIndexesHtml' => $foreignKeySupported ? Index::getHtmlForDisplayIndexes() : null,
216
                'route' => $route,
217
            ])
218
        );
219
    }
220
221
    /**
222
     * Update for display field
223
     *
224
     * @return void
225
     */
226
    public function updateForDisplayField()
227
    {
228
        if ($this->upd_query->updateDisplayField(
229
            $_POST['display_field'],
230
            $this->cfgRelation
231
        )
232
        ) {
233
            $this->response->addHTML(
234
                Generator::getMessage(
235
                    __('Display column was successfully updated.'),
236
                    '',
237
                    'success'
238
                )
239
            );
240
        }
241
    }
242
243
    /**
244
     * Update for FK
245
     *
246
     * @return void
247
     */
248
    public function updateForForeignKeysAction()
249
    {
250
        $multi_edit_columns_name = isset($_POST['foreign_key_fields_name'])
251
            ? $_POST['foreign_key_fields_name']
252
            : null;
253
        $preview_sql_data = '';
254
        $seen_error = false;
255
256
        // (for now, one index name only; we keep the definitions if the
257
        // foreign db is not the same)
258
        if (isset($_POST['destination_foreign_db'], $_POST['destination_foreign_table'])
259
            && isset($_POST['destination_foreign_column'])) {
260
            [$html, $preview_sql_data, $display_query, $seen_error]
261
                = $this->upd_query->updateForeignKeys(
262
                    $_POST['destination_foreign_db'],
263
                    $multi_edit_columns_name,
0 ignored issues
show
Bug introduced by
It seems like $multi_edit_columns_name can also be of type null; however, parameter $multi_edit_columns_name of PhpMyAdmin\Table::updateForeignKeys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

263
                    /** @scrutinizer ignore-type */ $multi_edit_columns_name,
Loading history...
264
                    $_POST['destination_foreign_table'],
265
                    $_POST['destination_foreign_column'],
266
                    $this->options_array,
267
                    $this->table,
268
                    is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign)
0 ignored issues
show
Bug introduced by
It seems like is_array($this->existrel...eign_keys_data'] : null can also be of type null; however, parameter $existrel_foreign of PhpMyAdmin\Table::updateForeignKeys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

268
                    /** @scrutinizer ignore-type */ is_array($this->existrel_foreign) && array_key_exists('foreign_keys_data', $this->existrel_foreign)
Loading history...
269
                    ? $this->existrel_foreign['foreign_keys_data']
270
                    : null
271
                );
272
            $this->response->addHTML($html);
273
        }
274
275
        // If there is a request for SQL previewing.
276
        if (isset($_POST['preview_sql'])) {
277
            Core::previewSQL($preview_sql_data);
278
        }
279
280
        if (! empty($display_query) && ! $seen_error) {
281
            $GLOBALS['display_query'] = $display_query;
282
            $this->response->addHTML(
283
                Generator::getMessage(
284
                    __('Your SQL query has been executed successfully.'),
285
                    null,
286
                    'success'
287
                )
288
            );
289
        }
290
    }
291
292
    /**
293
     * Update for internal relation
294
     *
295
     * @return void
296
     */
297
    public function updateForInternalRelationAction()
298
    {
299
        $multi_edit_columns_name = isset($_POST['fields_name'])
300
            ? $_POST['fields_name']
301
            : null;
302
303
        if ($this->upd_query->updateInternalRelations(
304
            $multi_edit_columns_name,
0 ignored issues
show
Bug introduced by
It seems like $multi_edit_columns_name can also be of type null; however, parameter $multi_edit_columns_name of PhpMyAdmin\Table::updateInternalRelations() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

304
            /** @scrutinizer ignore-type */ $multi_edit_columns_name,
Loading history...
305
            $_POST['destination_db'],
306
            $_POST['destination_table'],
307
            $_POST['destination_column'],
308
            $this->cfgRelation,
309
            isset($this->existrel) ? $this->existrel : null
310
        )
311
        ) {
312
            $this->response->addHTML(
313
                Generator::getMessage(
314
                    __('Internal relationships were successfully updated.'),
315
                    '',
316
                    'success'
317
                )
318
            );
319
        }
320
    }
321
322
    /**
323
     * Send table columns for foreign table dropdown
324
     *
325
     * @return void
326
     *
327
     */
328
    public function getDropdownValueForTableAction()
329
    {
330
        $foreignTable = $_POST['foreignTable'];
331
        $table_obj = $this->dbi->getTable($_POST['foreignDb'], $foreignTable);
332
        // Since views do not have keys defined on them provide the full list of
333
        // columns
334
        if ($table_obj->isView()) {
335
            $columnList = $table_obj->getColumns(false, false);
336
        } else {
337
            $columnList = $table_obj->getIndexedColumns(false, false);
338
        }
339
        $columns = [];
340
        foreach ($columnList as $column) {
341
            $columns[] = htmlspecialchars($column);
342
        }
343
        if ($GLOBALS['cfg']['NaturalOrder']) {
344
            usort($columns, 'strnatcasecmp');
345
        }
346
        $this->response->addJSON('columns', $columns);
347
348
        // @todo should be: $server->db($db)->table($table)->primary()
349
        $primary = Index::getPrimary($foreignTable, $_POST['foreignDb']);
350
        if (false === $primary) {
351
            return;
352
        }
353
354
        $this->response->addJSON('primary', array_keys($primary->getColumns()));
355
    }
356
357
    /**
358
     * Send database selection values for dropdown
359
     *
360
     * @return void
361
     *
362
     */
363
    public function getDropdownValueForDbAction()
364
    {
365
        $tables = [];
366
        $foreign = isset($_POST['foreign']) && $_POST['foreign'] === 'true';
367
368
        if ($foreign) {
369
            $query = 'SHOW TABLE STATUS FROM '
370
                . Util::backquote($_POST['foreignDb']);
0 ignored issues
show
Bug introduced by
Are you sure PhpMyAdmin\Util::backquote($_POST['foreignDb']) of type array|mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

370
                . /** @scrutinizer ignore-type */ Util::backquote($_POST['foreignDb']);
Loading history...
371
            $tables_rs = $this->dbi->query(
372
                $query,
373
                DatabaseInterface::CONNECT_USER,
374
                DatabaseInterface::QUERY_STORE
375
            );
376
377
            while ($row = $this->dbi->fetchArray($tables_rs)) {
0 ignored issues
show
Bug introduced by
It seems like $tables_rs can also be of type false; however, parameter $result of PhpMyAdmin\DatabaseInterface::fetchArray() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

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

377
            while ($row = $this->dbi->fetchArray(/** @scrutinizer ignore-type */ $tables_rs)) {
Loading history...
378
                if (isset($row['Engine'])
379
                    &&  mb_strtoupper($row['Engine']) == $this->tbl_storage_engine
380
                ) {
381
                    $tables[] = htmlspecialchars($row['Name']);
382
                }
383
            }
384
        } else {
385
            $query = 'SHOW TABLES FROM '
386
                . Util::backquote($_POST['foreignDb']);
387
            $tables_rs = $this->dbi->query(
388
                $query,
389
                DatabaseInterface::CONNECT_USER,
390
                DatabaseInterface::QUERY_STORE
391
            );
392
            while ($row = $this->dbi->fetchArray($tables_rs)) {
393
                $tables[] = htmlspecialchars($row[0]);
394
            }
395
        }
396
        if ($GLOBALS['cfg']['NaturalOrder']) {
397
            usort($tables, 'strnatcasecmp');
398
        }
399
        $this->response->addJSON('tables', $tables);
400
    }
401
}
402