Completed
Push — master ( 94018a...6a6ebc )
by AD
13s
created

Table   C

Complexity

Total Complexity 68

Size/Duplication

Total Lines 663
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 96.24%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 68
lcom 1
cbo 4
dl 0
loc 663
ccs 205
cts 213
cp 0.9624
rs 5.2732
c 2
b 0
f 0

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A setName() 0 5 1
A getName() 0 4 1
A setOptions() 0 5 1
A getOptions() 0 4 1
A setAdapter() 0 5 1
A getAdapter() 0 4 1
A exists() 0 4 1
A drop() 0 4 1
A rename() 0 6 1
A setColumns() 0 4 1
A getColumns() 0 4 1
A setPendingColumns() 0 5 1
A getPendingColumns() 0 4 1
A setIndexes() 0 5 1
A getIndexes() 0 4 1
A setForeignKeys() 0 5 1
A getForeignKeys() 0 4 1
A setData() 0 5 1
A getData() 0 4 1
A reset() 0 7 1
B addColumn() 0 29 4
A removeColumn() 0 5 1
A renameColumn() 0 5 1
A changeColumn() 0 19 4
A hasColumn() 0 4 1
A addIndex() 0 17 3
A removeIndex() 0 5 1
A removeIndexByName() 0 5 1
A hasIndex() 0 4 1
A addForeignKey() 0 18 3
A dropForeignKey() 0 13 3
A hasForeignKey() 0 4 1
A addTimestamps() 0 15 3
A insert() 0 12 4
A create() 0 6 1
B update() 0 22 5
B saveData() 0 26 6
A save() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like Table often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Table, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Phinx
4
 *
5
 * (The MIT license)
6
 * Copyright (c) 2015 Rob Morgan
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated * documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
25
 *
26
 * @package    Phinx
27
 * @subpackage Phinx\Db
28
 */
29
namespace Phinx\Db;
30
31
use Phinx\Db\Table\Column;
32
use Phinx\Db\Table\Index;
33
use Phinx\Db\Table\ForeignKey;
34
use Phinx\Db\Adapter\AdapterInterface;
35
36
/**
37
 *
38
 * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
39
 */
40
class Table
41
{
42
    /**
43
     * @var string
44
     */
45
    protected $name;
46
47
    /**
48
     * @var array
49
     */
50
    protected $options = [];
51
52
    /**
53
     * @var AdapterInterface
54
     */
55
    protected $adapter;
56
57
    /**
58
     * @var array
59
     */
60
    protected $columns = [];
61
62
    /**
63
     * @var array
64
     */
65
    protected $indexes = [];
66
67
    /**
68
     * @var ForeignKey[]
69
     */
70
    protected $foreignKeys = [];
71
72
    /**
73
     * @var array
74
     */
75
    protected $data = [];
76
77
    /**
78
     * Class Constuctor.
79
     *
80
     * @param string $name Table Name
81
     * @param array $options Options
82
     * @param AdapterInterface $adapter Database Adapter
83
     */
84 239
    public function __construct($name, $options = [], AdapterInterface $adapter = null)
85
    {
86 239
        $this->setName($name);
87 239
        $this->setOptions($options);
88
89 239
        if ($adapter !== null) {
90 231
            $this->setAdapter($adapter);
91 231
        }
92 239
    }
93
94
    /**
95
     * Sets the table name.
96
     *
97
     * @param string $name Table Name
98
     * @return Table
99
     */
100 239
    public function setName($name)
101
    {
102 239
        $this->name = $name;
103 239
        return $this;
104
    }
105
106
    /**
107
     * Gets the table name.
108
     *
109
     * @return string
110
     */
111 215
    public function getName()
112
    {
113 215
        return $this->name;
114
    }
115
116
    /**
117
     * Sets the table options.
118
     *
119
     * @param array $options
120
     * @return Table
121
     */
122 239
    public function setOptions($options)
123
    {
124 239
        $this->options = $options;
125 239
        return $this;
126
    }
127
128
    /**
129
     * Gets the table options.
130
     *
131
     * @return array
132
     */
133 189
    public function getOptions()
134
    {
135 189
        return $this->options;
136
    }
137
138
    /**
139
     * Sets the database adapter.
140
     *
141
     * @param AdapterInterface $adapter Database Adapter
142
     * @return Table
143
     */
144 231
    public function setAdapter(AdapterInterface $adapter)
145
    {
146 231
        $this->adapter = $adapter;
147 231
        return $this;
148
    }
149
150
    /**
151
     * Gets the database adapter.
152
     *
153
     * @return AdapterInterface
154
     */
155 225
    public function getAdapter()
156
    {
157 225
        return $this->adapter;
158
    }
159
160
    /**
161
     * Does the table exist?
162
     *
163
     * @return boolean
164
     */
165 195
    public function exists()
166
    {
167 195
        return $this->getAdapter()->hasTable($this->getName());
168
    }
169
170
    /**
171
     * Drops the database table.
172
     *
173
     * @return void
174
     */
175 1
    public function drop()
176
    {
177 1
        $this->getAdapter()->dropTable($this->getName());
178 1
    }
179
180
    /**
181
     * Renames the database table.
182
     *
183
     * @param string $newTableName New Table Name
184
     * @return Table
185
     */
186 3
    public function rename($newTableName)
187
    {
188 3
        $this->getAdapter()->renameTable($this->getName(), $newTableName);
189 3
        $this->setName($newTableName);
190 3
        return $this;
191
    }
192
193
    /**
194
     * Sets an array of columns waiting to be committed.
195
     * Use setPendingColumns
196
     *
197
     * @deprecated
198
     * @param array $columns Columns
199
     * @return Table
200
     */
201
    public function setColumns($columns)
202
    {
203
        $this->setPendingColumns($columns);
204
    }
205
206
    /**
207
     * Gets an array of the table columns.
208
     *
209
     * @return Column[]
210
     */
211 10
    public function getColumns()
212
    {
213 10
        return $this->getAdapter()->getColumns($this->getName());
214
    }
215
216
    /**
217
     * Sets an array of columns waiting to be committed.
218
     *
219
     * @param array $columns Columns
220
     * @return Table
221
     */
222 196
    public function setPendingColumns($columns)
223
    {
224 196
        $this->columns = $columns;
225 196
        return $this;
226
    }
227
228
    /**
229
     * Gets an array of columns waiting to be committed.
230
     *
231
     * @return Column[]
232
     */
233 204
    public function getPendingColumns()
234
    {
235 204
        return $this->columns;
236
    }
237
238
    /**
239
     * Sets an array of columns waiting to be indexed.
240
     *
241
     * @param array $indexes Indexes
242
     * @return Table
243
     */
244 196
    public function setIndexes($indexes)
245
    {
246 196
        $this->indexes = $indexes;
247 196
        return $this;
248
    }
249
250
    /**
251
     * Gets an array of indexes waiting to be committed.
252
     *
253
     * @return array
254
     */
255 191
    public function getIndexes()
256
    {
257 191
        return $this->indexes;
258
    }
259
260
    /**
261
     * Sets an array of foreign keys waiting to be commited.
262
     *
263
     * @param ForeignKey[] $foreignKeys foreign keys
264
     * @return Table
265
     */
266 196
    public function setForeignKeys($foreignKeys)
267
    {
268 196
        $this->foreignKeys = $foreignKeys;
269 196
        return $this;
270
    }
271
272
    /**
273
     * Gets an array of foreign keys waiting to be commited.
274
     *
275
     * @return array|ForeignKey[]
276
     */
277 192
    public function getForeignKeys()
278
    {
279 192
        return $this->foreignKeys;
280
    }
281
282
    /**
283
     * Sets an array of data to be inserted.
284
     *
285
     * @param array $data Data
286
     * @return Table
287
     */
288 196
    public function setData($data)
289
    {
290 196
        $this->data = $data;
291 196
        return $this;
292
    }
293
294
    /**
295
     * Gets the data waiting to be inserted.
296
     *
297
     * @return array
298
     */
299 197
    public function getData()
300
    {
301 197
        return $this->data;
302
    }
303
304
    /**
305
     * Resets all of the pending table changes.
306
     *
307
     * @return void
308
     */
309 196
    public function reset()
310
    {
311 196
        $this->setPendingColumns([]);
312 196
        $this->setIndexes([]);
313 196
        $this->setForeignKeys([]);
314 196
        $this->setData([]);
315 196
    }
316
317
    /**
318
     * Add a table column.
319
     *
320
     * Type can be: string, text, integer, float, decimal, datetime, timestamp,
321
     * time, date, binary, boolean.
322
     *
323
     * Valid options can be: limit, default, null, precision or scale.
324
     *
325
     * @param string|Column $columnName Column Name
326
     * @param string $type Column Type
327
     * @param array $options Column Options
328
     * @throws \RuntimeException
329
     * @throws \InvalidArgumentException
330
     * @return Table
331
     */
332 210
    public function addColumn($columnName, $type = null, $options = [])
333
    {
334
        // we need an adapter set to add a column
335 210
        if ($this->getAdapter() === null) {
336 1
            throw new \RuntimeException('An adapter must be specified to add a column.');
337
        }
338
339
        // create a new column object if only strings were supplied
340 209
        if (!$columnName instanceof Column) {
341 207
            $column = new Column();
342 207
            $column->setName($columnName);
343 207
            $column->setType($type);
344 207
            $column->setOptions($options); // map options to column methods
345 207
        } else {
346 2
            $column = $columnName;
347
        }
348
349
        // Delegate to Adapters to check column type
350 209
        if (!$this->getAdapter()->isValidColumnType($column)) {
351 1
            throw new \InvalidArgumentException(sprintf(
352 1
                'An invalid column type "%s" was specified for column "%s".',
353 1
                $column->getType(),
354 1
                $column->getName()
355 1
            ));
356
        }
357
358 208
        $this->columns[] = $column;
359 208
        return $this;
360
    }
361
362
    /**
363
     * Remove a table column.
364
     *
365
     * @param string $columnName Column Name
366
     * @return Table
367
     */
368 1
    public function removeColumn($columnName)
369
    {
370 1
        $this->getAdapter()->dropColumn($this->getName(), $columnName);
371 1
        return $this;
372
    }
373
374
    /**
375
     * Rename a table column.
376
     *
377
     * @param string $oldName Old Column Name
378
     * @param string $newName New Column Name
379
     * @return Table
380
     */
381 4
    public function renameColumn($oldName, $newName)
382
    {
383 4
        $this->getAdapter()->renameColumn($this->getName(), $oldName, $newName);
384 4
        return $this;
385
    }
386
387
    /**
388
     * Change a table column type.
389
     *
390
     * @param string        $columnName    Column Name
391
     * @param string|Column $newColumnType New Column Type
392
     * @param array         $options       Options
393
     * @return Table
394
     */
395 17
    public function changeColumn($columnName, $newColumnType, $options = [])
396
    {
397
        // create a column object if one wasn't supplied
398 17
        if (!$newColumnType instanceof Column) {
399 4
            $newColumn = new Column();
400 4
            $newColumn->setType($newColumnType);
401 4
            $newColumn->setOptions($options);
402 4
        } else {
403 13
            $newColumn = $newColumnType;
404
        }
405
406
        // if the name was omitted use the existing column name
407 17
        if ($newColumn->getName() === null || strlen($newColumn->getName()) === 0) {
408 15
            $newColumn->setName($columnName);
409 15
        }
410
411 17
        $this->getAdapter()->changeColumn($this->getName(), $columnName, $newColumn);
412 17
        return $this;
413
    }
414
415
    /**
416
     * Checks to see if a column exists.
417
     *
418
     * @param string $columnName Column Name
419
     * @return boolean
420
     */
421 89
    public function hasColumn($columnName)
422
    {
423 89
        return $this->getAdapter()->hasColumn($this->getName(), $columnName);
424
    }
425
426
    /**
427
     * Add an index to a database table.
428
     *
429
     * In $options you can specific unique = true/false or name (index name).
430
     *
431
     * @param string|array|Index $columns Table Column(s)
432
     * @param array $options Index Options
433
     * @return Table
434
     */
435 29
    public function addIndex($columns, $options = [])
436
    {
437
        // create a new index object if strings or an array of strings were supplied
438 29
        if (!$columns instanceof Index) {
439 28
            $index = new Index();
440 28
            if (is_string($columns)) {
441 22
                $columns = [$columns]; // str to array
442 22
            }
443 28
            $index->setColumns($columns);
444 28
            $index->setOptions($options);
445 28
        } else {
446 1
            $index = $columns;
447
        }
448
449 29
        $this->indexes[] = $index;
450 29
        return $this;
451
    }
452
453
    /**
454
     * Removes the given index from a table.
455
     *
456
     * @param array $columns Columns
457
     * @return Table
458
     */
459 1
    public function removeIndex($columns)
460
    {
461 1
        $this->getAdapter()->dropIndex($this->getName(), $columns);
462 1
        return $this;
463
    }
464
465
    /**
466
     * Removes the given index identified by its name from a table.
467
     *
468
     * @param string $name Index name
469
     * @return Table
470
     */
471 1
    public function removeIndexByName($name)
472
    {
473 1
        $this->getAdapter()->dropIndexByName($this->getName(), $name);
474 1
        return $this;
475
    }
476
477
    /**
478
     * Checks to see if an index exists.
479
     *
480
     * @param string|array $columns Columns
481
     * @param array        $options Options
0 ignored issues
show
Bug introduced by
There is no parameter named $options. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
482
     * @return boolean
483
     */
484 12
    public function hasIndex($columns)
485
    {
486 12
        return $this->getAdapter()->hasIndex($this->getName(), $columns);
487
    }
488
489
    /**
490
     * Add a foreign key to a database table.
491
     *
492
     * In $options you can specify on_delete|on_delete = cascade|no_action ..,
493
     * on_update, constraint = constraint name.
494
     *
495
     * @param string|array $columns Columns
496
     * @param string|Table $referencedTable   Referenced Table
497
     * @param string|array $referencedColumns Referenced Columns
498
     * @param array $options Options
499
     * @return Table
500
     */
501 8
    public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], $options = [])
502
    {
503 8
        if (is_string($referencedColumns)) {
504 4
            $referencedColumns = [$referencedColumns]; // str to array
505 4
        }
506 8
        $fk = new ForeignKey();
507 8
        if ($referencedTable instanceof Table) {
508
            $fk->setReferencedTable($referencedTable);
509
        } else {
510 8
            $fk->setReferencedTable(new Table($referencedTable, [], $this->adapter));
511
        }
512 8
        $fk->setColumns($columns)
513 8
           ->setReferencedColumns($referencedColumns)
514 8
           ->setOptions($options);
515 8
        $this->foreignKeys[] = $fk;
516
517 8
        return $this;
518
    }
519
520
    /**
521
     * Removes the given foreign key from the table.
522
     *
523
     * @param string|array $columns    Column(s)
524
     * @param null|string  $constraint Constraint names
525
     * @return Table
526
     */
527 1
    public function dropForeignKey($columns, $constraint = null)
528
    {
529 1
        if (is_string($columns)) {
530 1
            $columns = [$columns];
531 1
        }
532 1
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
533
            $this->getAdapter()->dropForeignKey($this->getName(), [], $constraint);
534
        } else {
535 1
            $this->getAdapter()->dropForeignKey($this->getName(), $columns);
536
        }
537
538 1
        return $this;
539
    }
540
541
    /**
542
     * Checks to see if a foreign key exists.
543
     *
544
     * @param  string|array $columns    Column(s)
545
     * @param  null|string  $constraint Constraint names
546
     * @return boolean
547
     */
548 1
    public function hasForeignKey($columns, $constraint = null)
549
    {
550 1
        return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint);
0 ignored issues
show
Documentation introduced by
$columns is of type string|array, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
551
    }
552
553
    /**
554
     * Add timestamp columns created_at and updated_at to the table.
555
     *
556
     * @param string $createdAtColumnName
557
     * @param string $updatedAtColumnName
558
     *
559
     * @return Table
560
     */
561 15
    public function addTimestamps($createdAtColumnName = 'created_at', $updatedAtColumnName = 'updated_at')
562
    {
563 15
        $createdAtColumnName = is_null($createdAtColumnName) ? 'created_at' : $createdAtColumnName;
564 15
        $updatedAtColumnName = is_null($updatedAtColumnName) ? 'updated_at' : $updatedAtColumnName;
565 15
        $this->addColumn($createdAtColumnName, 'timestamp', [
566 15
                'default' => 'CURRENT_TIMESTAMP',
567
                'update' => ''
568 15
            ])
569 15
             ->addColumn($updatedAtColumnName, 'timestamp', [
570 15
                'null'    => true,
571
                'default' => null
572 15
             ]);
573
574 15
        return $this;
575
    }
576
577
    /**
578
     * Insert data into the table.
579
     *
580
     * @param $data array of data in the form:
581
     *              array(
582
     *                  array("col1" => "value1", "col2" => "anotherValue1"),
583
     *                  array("col2" => "value2", "col2" => "anotherValue2"),
584
     *              )
585
     *              or array("col1" => "value1", "col2" => "anotherValue1")
586
     *
587
     * @return Table
588
     */
589 17
    public function insert($data)
590
    {
591
        // handle array of array situations
592 17
        if (isset($data[0]) && is_array($data[0])) {
593 11
            foreach ($data as $row) {
594 11
                $this->data[] = $row;
595 11
            }
596 11
            return $this;
597
        }
598 8
        $this->data[] = $data;
599 8
        return $this;
600
    }
601
602
    /**
603
     * Creates a table from the object instance.
604
     *
605
     * @return void
606
     */
607 196
    public function create()
608
    {
609 196
        $this->getAdapter()->createTable($this);
610 196
        $this->saveData();
611 196
        $this->reset(); // reset pending changes
612 196
    }
613
614
    /**
615
     * Updates a table from the object instance.
616
     *
617
     * @throws \RuntimeException
618
     * @return void
619
     */
620 46
    public function update()
621
    {
622 46
        if (!$this->exists()) {
623
            throw new \RuntimeException('Cannot update a table that doesn\'t exist!');
624
        }
625
626
        // update table
627 46
        foreach ($this->getPendingColumns() as $column) {
628 38
            $this->getAdapter()->addColumn($this, $column);
629 46
        }
630
631 46
        foreach ($this->getIndexes() as $index) {
632 6
            $this->getAdapter()->addIndex($this, $index);
633 46
        }
634
635 46
        foreach ($this->getForeignKeys() as $foreignKey) {
636 3
            $this->getAdapter()->addForeignKey($this, $foreignKey);
637 46
        }
638
639 46
        $this->saveData();
640 46
        $this->reset(); // reset pending changes
641 46
    }
642
643
    /**
644
     * Commit the pending data waiting for insertion.
645
     *
646
     * @return void
647
     */
648 196
    public function saveData()
649
    {
650 196
        $rows = $this->getData();
651 196
        if (empty($rows)) {
652 192
            return;
653
        }
654
655 12
        $bulk = true;
656 12
        $row = current($rows);
657 12
        $c = array_keys($row);
658 12
        foreach ($this->getData() as $row) {
659 12
            $k = array_keys($row);
660 12
            if ($k != $c) {
661 1
                $bulk = false;
662 1
                break;
663
            }
664 12
        }
665
666 12
        if ($bulk) {
667 11
            $this->getAdapter()->bulkinsert($this, $this->getData());
668 11
        } else {
669 1
            foreach ($this->getData() as $row) {
670 1
                $this->getAdapter()->insert($this, $row);
671 1
            }
672
        }
673 12
    }
674
675
    /**
676
     * Truncates the table.
677
     *
678
     * @return void
679
     */
680 2
    public function truncate()
681
    {
682 2
        $this->getAdapter()->truncateTable($this->getName());
683 2
    }
684
685
    /**
686
     * Commits the table changes.
687
     *
688
     * If the table doesn't exist it is created otherwise it is updated.
689
     *
690
     * @return void
691
     */
692 195
    public function save()
693
    {
694 195
        if ($this->exists()) {
695 45
            $this->update(); // update the table
696 45
        } else {
697 195
            $this->create(); // create the table
698
        }
699
700 195
        $this->reset(); // reset pending changes
701 195
    }
702
}
703