Completed
Pull Request — master (#1175)
by David Joseph
04:09
created

Table::addCustomColumn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
ccs 7
cts 7
cp 1
rs 9.4285
cc 2
eloc 10
nc 2
nop 3
crap 2
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\Adapter\AdapterInterface;
32
use Phinx\Db\Table\Column;
33
use Phinx\Db\Table\CustomColumn;
34
use Phinx\Db\Table\ForeignKey;
35
use Phinx\Db\Table\Index;
36
37
/**
38
 *
39
 * This object is based loosely on: http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
40
 */
41
class Table
42
{
43
    /**
44
     * @var string
45
     */
46
    protected $name;
47
48
    /**
49
     * @var array
50
     */
51
    protected $options = [];
52
53
    /**
54
     * @var \Phinx\Db\Adapter\AdapterInterface
55
     */
56
    protected $adapter;
57
58
    /**
59
     * @var array
60
     */
61
    protected $columns = [];
62
63
    /**
64
     * @var array
65
     */
66
    protected $indexes = [];
67
68
    /**
69
     * @var \Phinx\Db\Table\ForeignKey[]
70
     */
71
    protected $foreignKeys = [];
72
73
    /**
74
     * @var array
75
     */
76
    protected $data = [];
77
78
    /**
79
     * Class Constuctor.
80
     *
81
     * @param string $name Table Name
82
     * @param array $options Options
83
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
84 239
     */
85
    public function __construct($name, $options = [], AdapterInterface $adapter = null)
86 239
    {
87 239
        $this->setName($name);
88
        $this->setOptions($options);
89 239
90 231
        if ($adapter !== null) {
91 231
            $this->setAdapter($adapter);
92 239
        }
93
    }
94
95
    /**
96
     * Sets the table name.
97
     *
98
     * @param string $name Table Name
99
     * @return \Phinx\Db\Table
100 239
     */
101
    public function setName($name)
102 239
    {
103 239
        $this->name = $name;
104
105
        return $this;
106
    }
107
108
    /**
109
     * Gets the table name.
110
     *
111 215
     * @return string
112
     */
113 215
    public function getName()
114
    {
115
        return $this->name;
116
    }
117
118
    /**
119
     * Sets the table options.
120
     *
121
     * @param array $options
122 239
     * @return \Phinx\Db\Table
123
     */
124 239
    public function setOptions($options)
125 239
    {
126
        $this->options = $options;
127
128
        return $this;
129
    }
130
131
    /**
132
     * Gets the table options.
133 189
     *
134
     * @return array
135 189
     */
136
    public function getOptions()
137
    {
138
        return $this->options;
139
    }
140
141
    /**
142
     * Sets the database adapter.
143
     *
144 231
     * @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
145
     * @return \Phinx\Db\Table
146 231
     */
147 231
    public function setAdapter(AdapterInterface $adapter)
148
    {
149
        $this->adapter = $adapter;
150
151
        return $this;
152
    }
153
154
    /**
155 225
     * Gets the database adapter.
156
     *
157 225
     * @return \Phinx\Db\Adapter\AdapterInterface
158
     */
159
    public function getAdapter()
160
    {
161
        return $this->adapter;
162
    }
163
164
    /**
165 195
     * Does the table exist?
166
     *
167 195
     * @return bool
168
     */
169
    public function exists()
170
    {
171
        return $this->getAdapter()->hasTable($this->getName());
172
    }
173
174
    /**
175 1
     * Drops the database table.
176
     *
177 1
     * @return void
178 1
     */
179
    public function drop()
180
    {
181
        $this->getAdapter()->dropTable($this->getName());
182
    }
183
184
    /**
185
     * Renames the database table.
186 3
     *
187
     * @param string $newTableName New Table Name
188 3
     * @return \Phinx\Db\Table
189 3
     */
190 3
    public function rename($newTableName)
191
    {
192
        $this->getAdapter()->renameTable($this->getName(), $newTableName);
193
        $this->setName($newTableName);
194
195
        return $this;
196
    }
197
198
    /**
199
     * Sets an array of columns waiting to be committed.
200
     * Use setPendingColumns
201
     *
202
     * @deprecated
203
     * @param array $columns Columns
204
     * @return \Phinx\Db\Table
205
     */
206
    public function setColumns($columns)
207
    {
208
        $this->setPendingColumns($columns);
209
210
        return $this;
211 10
    }
212
213 10
    /**
214
     * Gets an array of the table columns.
215
     *
216
     * @return \Phinx\Db\Table\Column[]
217
     */
218
    public function getColumns()
219
    {
220
        return $this->getAdapter()->getColumns($this->getName());
221
    }
222 196
223
    /**
224 196
     * Sets an array of columns waiting to be committed.
225 196
     *
226
     * @param array $columns Columns
227
     * @return \Phinx\Db\Table
228
     */
229
    public function setPendingColumns($columns)
230
    {
231
        $this->columns = $columns;
232
233 204
        return $this;
234
    }
235 204
236
    /**
237
     * Gets an array of columns waiting to be committed.
238
     *
239
     * @return \Phinx\Db\Table\Column[]
240
     */
241
    public function getPendingColumns()
242
    {
243
        return $this->columns;
244 196
    }
245
246 196
    /**
247 196
     * Sets an array of columns waiting to be indexed.
248
     *
249
     * @param array $indexes Indexes
250
     * @return \Phinx\Db\Table
251
     */
252
    public function setIndexes($indexes)
253
    {
254
        $this->indexes = $indexes;
255 191
256
        return $this;
257 191
    }
258
259
    /**
260
     * Gets an array of indexes waiting to be committed.
261
     *
262
     * @return array
263
     */
264
    public function getIndexes()
265
    {
266 196
        return $this->indexes;
267
    }
268 196
269 196
    /**
270
     * Sets an array of foreign keys waiting to be commited.
271
     *
272
     * @param \Phinx\Db\Table\ForeignKey[] $foreignKeys foreign keys
273
     * @return \Phinx\Db\Table
274
     */
275
    public function setForeignKeys($foreignKeys)
276
    {
277 192
        $this->foreignKeys = $foreignKeys;
278
279 192
        return $this;
280
    }
281
282
    /**
283
     * Gets an array of foreign keys waiting to be commited.
284
     *
285
     * @return array|\Phinx\Db\Table\ForeignKey[]
286
     */
287
    public function getForeignKeys()
288 196
    {
289
        return $this->foreignKeys;
290 196
    }
291 196
292
    /**
293
     * Sets an array of data to be inserted.
294
     *
295
     * @param array $data Data
296
     * @return \Phinx\Db\Table
297
     */
298
    public function setData($data)
299 197
    {
300
        $this->data = $data;
301 197
302
        return $this;
303
    }
304
305
    /**
306
     * Gets the data waiting to be inserted.
307
     *
308
     * @return array
309 196
     */
310
    public function getData()
311 196
    {
312 196
        return $this->data;
313 196
    }
314 196
315 196
    /**
316
     * Resets all of the pending table changes.
317
     *
318
     * @return void
319
     */
320
    public function reset()
321
    {
322
        $this->setPendingColumns([]);
323
        $this->setIndexes([]);
324
        $this->setForeignKeys([]);
325
        $this->setData([]);
326
    }
327
328
    /**
329
     * Add a table column.
330
     *
331
     * Type can be: string, text, integer, float, decimal, datetime, timestamp,
332 210
     * time, date, binary, boolean.
333
     *
334
     * Valid options can be: limit, default, null, precision or scale.
335 210
     *
336 1
     * @param string|\Phinx\Db\Table\Column $columnName Column Name
337
     * @param string $type Column Type
338
     * @param array $options Column Options
339
     * @throws \RuntimeException
340 209
     * @throws \InvalidArgumentException
341 207
     * @return \Phinx\Db\Table
342 207
     */
343 207
    public function addColumn($columnName, $type = null, $options = [])
344 207
    {
345 207
        // we need an adapter set to add a column
346 2
        if ($this->getAdapter() === null) {
347
            throw new \RuntimeException('An adapter must be specified to add a column.');
348
        }
349
350 209
        // create a new column object if only strings were supplied
351 1
        if (!$columnName instanceof Column) {
352 1
            $column = new Column();
353 1
            $column->setName($columnName);
354 1
            $column->setType($type);
355 1
            $column->setOptions($options); // map options to column methods
356
        } elseif ($columnName instanceof CustomColumn) {
357
            return $this->addCustomColumn($columnName, $type, $options);
358 208
        } else {
359 208
            $column = $columnName;
360
        }
361
362
        // Delegate to Adapters to check column type
363
        if (!$this->getAdapter()->isValidColumnType($column)) {
364
            throw new \InvalidArgumentException(sprintf(
365
                'An invalid column type "%s" was specified for column "%s".',
366
                $column->getType(),
367
                $column->getName()
368 1
            ));
369
        }
370 1
371 1
        $this->columns[] = $column;
372
373
        return $this;
374
    }
375
376
    /**
377
     * Add a table column with a custom type definition.
378
     *
379
     * Type can be any string that you expect your DBMS to understand
380
     *
381 4
     * Valid options can be:  default or null.
382
     *
383 4
     * For widely supported types @see addColumn
384 4
     *
385
     * @param string|CustomColumn $columnName Column Name
386
     * @param string $type Column Type
387
     * @param array $options Column Options
388
     * @return Table
389
     */
390
    public function addCustomColumn($columnName, $type = null, $options = [])
391
    {
392
        // create a new CustomColumn object if only strings were supplied
393
        if (!$columnName instanceof CustomColumn) {
394
            $column = new CustomColumn();
395 17
            $column->setName($columnName);
396
            $column->setType($type);
397
            $column->setOptions($options); // map options to column methods
398 17
        } else {
399 4
            $column = $columnName;
400 4
        }
401 4
402 4
        $this->columns[] = $column;
403 13
404
        return $this;
405
    }
406
407 17
    /**
408 15
     * Remove a table column.
409 15
     *
410
     * @param string $columnName Column Name
411 17
     * @return \Phinx\Db\Table
412 17
     */
413
    public function removeColumn($columnName)
414
    {
415
        $this->getAdapter()->dropColumn($this->getName(), $columnName);
416
417
        return $this;
418
    }
419
420
    /**
421 89
     * Rename a table column.
422
     *
423 89
     * @param string $oldName Old Column Name
424
     * @param string $newName New Column Name
425
     * @return \Phinx\Db\Table
426
     */
427
    public function renameColumn($oldName, $newName)
428
    {
429
        $this->getAdapter()->renameColumn($this->getName(), $oldName, $newName);
430
431
        return $this;
432
    }
433
434
    /**
435 29
     * Change a table column type.
436
     *
437
     * @param string        $columnName    Column Name
438 29
     * @param string|\Phinx\Db\Table\Column $newColumnType New Column Type
439 28
     * @param array         $options       Options
440 28
     * @return \Phinx\Db\Table
441 22
     */
442 22
    public function changeColumn($columnName, $newColumnType, $options = [])
443 28
    {
444 28
        // create a column object if one wasn't supplied
445 28
        if (!$newColumnType instanceof Column) {
446 1
            $newColumn = new Column();
447
            $newColumn->setType($newColumnType);
448
            $newColumn->setOptions($options);
449 29
        } else {
450 29
            $newColumn = $newColumnType;
451
        }
452
453
        // if the name was omitted use the existing column name
454
        if ($newColumn->getName() === null || strlen($newColumn->getName()) === 0) {
455
            $newColumn->setName($columnName);
456
        }
457
458
        $this->getAdapter()->changeColumn($this->getName(), $columnName, $newColumn);
459 1
460
        return $this;
461 1
    }
462 1
463
    /**
464
     * Checks to see if a column exists.
465
     *
466
     * @param string $columnName Column Name
467
     * @return bool
468
     */
469
    public function hasColumn($columnName)
470
    {
471 1
        return $this->getAdapter()->hasColumn($this->getName(), $columnName);
472
    }
473 1
474 1
    /**
475
     * Add an index to a database table.
476
     *
477
     * In $options you can specific unique = true/false or name (index name).
478
     *
479
     * @param string|array|\Phinx\Db\Table\Index $columns Table Column(s)
480
     * @param array $options Index Options
481
     * @return \Phinx\Db\Table
482
     */
483
    public function addIndex($columns, $options = [])
484 12
    {
485
        // create a new index object if strings or an array of strings were supplied
486 12
        if (!$columns instanceof Index) {
487
            $index = new Index();
488
            if (is_string($columns)) {
489
                $columns = [$columns]; // str to array
490
            }
491
            $index->setColumns($columns);
492
            $index->setOptions($options);
493
        } else {
494
            $index = $columns;
495
        }
496
497
        $this->indexes[] = $index;
498
499
        return $this;
500
    }
501 8
502
    /**
503 8
     * Removes the given index from a table.
504 4
     *
505 4
     * @param array $columns Columns
506 8
     * @return \Phinx\Db\Table
507 8
     */
508
    public function removeIndex($columns)
509
    {
510 8
        $this->getAdapter()->dropIndex($this->getName(), $columns);
511
512 8
        return $this;
513 8
    }
514 8
515 8
    /**
516
     * Removes the given index identified by its name from a table.
517 8
     *
518
     * @param string $name Index name
519
     * @return \Phinx\Db\Table
520
     */
521
    public function removeIndexByName($name)
522
    {
523
        $this->getAdapter()->dropIndexByName($this->getName(), $name);
524
525
        return $this;
526
    }
527 1
528
    /**
529 1
     * Checks to see if an index exists.
530 1
     *
531 1
     * @param string|array $columns Columns
532 1
     * @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...
533
     * @return bool
534
     */
535 1
    public function hasIndex($columns)
536
    {
537
        return $this->getAdapter()->hasIndex($this->getName(), $columns);
538 1
    }
539
540
    /**
541
     * Add a foreign key to a database table.
542
     *
543
     * In $options you can specify on_delete|on_delete = cascade|no_action ..,
544
     * on_update, constraint = constraint name.
545
     *
546
     * @param string|array $columns Columns
547
     * @param string|\Phinx\Db\Table $referencedTable   Referenced Table
548 1
     * @param string|array $referencedColumns Referenced Columns
549
     * @param array $options Options
550 1
     * @return \Phinx\Db\Table
551
     */
552
    public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], $options = [])
553
    {
554
        if (is_string($referencedColumns)) {
555
            $referencedColumns = [$referencedColumns]; // str to array
556
        }
557
        $fk = new ForeignKey();
558
        if ($referencedTable instanceof Table) {
559
            $fk->setReferencedTable($referencedTable);
560
        } else {
561 15
            $fk->setReferencedTable(new Table($referencedTable, [], $this->adapter));
562
        }
563 15
        $fk->setColumns($columns)
564 15
           ->setReferencedColumns($referencedColumns)
565 15
           ->setOptions($options);
566 15
        $this->foreignKeys[] = $fk;
567
568 15
        return $this;
569 15
    }
570 15
571
    /**
572 15
     * Removes the given foreign key from the table.
573
     *
574 15
     * @param string|array $columns    Column(s)
575
     * @param null|string  $constraint Constraint names
576
     * @return \Phinx\Db\Table
577
     */
578
    public function dropForeignKey($columns, $constraint = null)
579
    {
580
        if (is_string($columns)) {
581
            $columns = [$columns];
582
        }
583
        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...
584
            $this->getAdapter()->dropForeignKey($this->getName(), [], $constraint);
585
        } else {
586
            $this->getAdapter()->dropForeignKey($this->getName(), $columns);
587
        }
588
589 17
        return $this;
590
    }
591
592 17
    /**
593 11
     * Checks to see if a foreign key exists.
594 11
     *
595 11
     * @param  string|array $columns    Column(s)
596 11
     * @param  null|string  $constraint Constraint names
597
     * @return bool
598 8
     */
599 8
    public function hasForeignKey($columns, $constraint = null)
600
    {
601
        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...
602
    }
603
604
    /**
605
     * Add timestamp columns created_at and updated_at to the table.
606
     *
607 196
     * @param string $createdAtColumnName
608
     * @param string $updatedAtColumnName
609 196
     *
610 196
     * @return \Phinx\Db\Table
611 196
     */
612 196
    public function addTimestamps($createdAtColumnName = 'created_at', $updatedAtColumnName = 'updated_at')
613
    {
614
        $createdAtColumnName = is_null($createdAtColumnName) ? 'created_at' : $createdAtColumnName;
615
        $updatedAtColumnName = is_null($updatedAtColumnName) ? 'updated_at' : $updatedAtColumnName;
616
        $this->addColumn($createdAtColumnName, 'timestamp', [
617
                'default' => 'CURRENT_TIMESTAMP',
618
                'update' => ''
619
            ])
620 46
             ->addColumn($updatedAtColumnName, 'timestamp', [
621
                 'null' => true,
622 46
                 'default' => null
623
             ]);
624
625
        return $this;
626
    }
627 46
628 38
    /**
629 46
     * Insert data into the table.
630
     *
631 46
     * @param array $data array of data in the form:
632 6
     *              array(
633 46
     *                  array("col1" => "value1", "col2" => "anotherValue1"),
634
     *                  array("col2" => "value2", "col2" => "anotherValue2"),
635 46
     *              )
636 3
     *              or array("col1" => "value1", "col2" => "anotherValue1")
637 46
     *
638
     * @return \Phinx\Db\Table
639 46
     */
640 46
    public function insert($data)
641 46
    {
642
        // handle array of array situations
643
        if (isset($data[0]) && is_array($data[0])) {
644
            foreach ($data as $row) {
645
                $this->data[] = $row;
646
            }
647
648 196
            return $this;
649
        }
650 196
        $this->data[] = $data;
651 196
652 192
        return $this;
653
    }
654
655 12
    /**
656 12
     * Creates a table from the object instance.
657 12
     *
658 12
     * @return void
659 12
     */
660 12
    public function create()
661 1
    {
662 1
        $this->getAdapter()->createTable($this);
663
        $this->saveData();
664 12
        $this->reset(); // reset pending changes
665
    }
666 12
667 11
    /**
668 11
     * Updates a table from the object instance.
669 1
     *
670 1
     * @throws \RuntimeException
671 1
     * @return void
672
     */
673 12
    public function update()
674
    {
675
        if (!$this->exists()) {
676
            throw new \RuntimeException('Cannot update a table that doesn\'t exist!');
677
        }
678
679
        // update table
680 2
        foreach ($this->getPendingColumns() as $column) {
681
            $this->getAdapter()->addColumn($this, $column);
682 2
        }
683 2
684
        foreach ($this->getIndexes() as $index) {
685
            $this->getAdapter()->addIndex($this, $index);
686
        }
687
688
        foreach ($this->getForeignKeys() as $foreignKey) {
689
            $this->getAdapter()->addForeignKey($this, $foreignKey);
690
        }
691
692 195
        $this->saveData();
693
        $this->reset(); // reset pending changes
694 195
    }
695 45
696 45
    /**
697 195
     * Commit the pending data waiting for insertion.
698
     *
699
     * @return void
700 195
     */
701 195
    public function saveData()
702
    {
703
        $rows = $this->getData();
704
        if (empty($rows)) {
705
            return;
706
        }
707
708
        $bulk = true;
709
        $row = current($rows);
710
        $c = array_keys($row);
711
        foreach ($this->getData() as $row) {
712
            $k = array_keys($row);
713
            if ($k != $c) {
714
                $bulk = false;
715
                break;
716
            }
717
        }
718
719
        if ($bulk) {
720
            $this->getAdapter()->bulkinsert($this, $this->getData());
721
        } else {
722
            foreach ($this->getData() as $row) {
723
                $this->getAdapter()->insert($this, $row);
724
            }
725
        }
726
    }
727
728
    /**
729
     * Truncates the table.
730
     *
731
     * @return void
732
     */
733
    public function truncate()
734
    {
735
        $this->getAdapter()->truncateTable($this->getName());
736
    }
737
738
    /**
739
     * Commits the table changes.
740
     *
741
     * If the table doesn't exist it is created otherwise it is updated.
742
     *
743
     * @return void
744
     */
745
    public function save()
746
    {
747
        if ($this->exists()) {
748
            $this->update(); // update the table
749
        } else {
750
            $this->create(); // create the table
751
        }
752
753
        $this->reset(); // reset pending changes
754
    }
755
}
756