• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

twinh / wei / 13100241610

02 Feb 2025 03:42PM UTC coverage: 89.225% (+0.005%) from 89.22%
13100241610

push

github

semantic-release-bot
chore(release): publish

See CHANGELOG.md for more details.

7072 of 7926 relevant lines covered (89.23%)

671.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.09
/lib/Schema.php
1
<?php
2

3
namespace Wei;
4

5
/**
6
 * A MySQL schema builder
7
 *
8
 * @property Db $db
9
 */
10
class Schema extends Base
11
{
12
    public const TYPE_BIG_INT = 'bigInt';
13

14
    public const TYPE_BOOL = 'bool';
15

16
    public const TYPE_CHAR = 'char';
17

18
    public const TYPE_DATE = 'date';
19

20
    public const TYPE_DATETIME = 'datetime';
21

22
    public const TYPE_DECIMAL = 'decimal';
23

24
    public const TYPE_DOUBLE = 'double';
25

26
    public const TYPE_INT = 'int';
27

28
    public const TYPE_LONG_TEXT = 'longText';
29

30
    public const TYPE_MEDIUM_INT = 'mediumInt';
31

32
    public const TYPE_MEDIUM_TEXT = 'mediumText';
33

34
    public const TYPE_TINY_INT = 'tinyInt';
35

36
    public const TYPE_SMALL_INT = 'smallInt';
37

38
    public const TYPE_STRING = 'string';
39

40
    public const TYPE_TEXT = 'text';
41

42
    public const TYPE_TIMESTAMP = 'timestamp';
43

44
    public const TYPE_JSON = 'json';
45

46
    public const TYPE_BINARY = 'binary';
47

48
    public const TYPE_VAR_BINARY = 'varBinary';
49

50
    /**
51
     * @var string
52
     */
53
    protected $table;
54

55
    /**
56
     * @var string
57
     */
58
    protected $engine = 'InnoDB';
59

60
    /**
61
     * @var string
62
     */
63
    protected $charset = '';
64

65
    /**
66
     * @var string
67
     */
68
    protected $collate = '';
69

70
    /**
71
     * @var string
72
     */
73
    protected $tableComment = '';
74

75
    /**
76
     * @var bool
77
     */
78
    protected $autoDefault = true;
79

80
    /**
81
     * @var bool
82
     */
83
    protected $defaultNullable = false;
84

85
    /**
86
     * The column type for user id column, like created_by and updated_by
87
     *
88
     * @var string
89
     */
90
    protected $userIdType = 'uInt';
91

92
    /**
93
     * @var string
94
     */
95
    protected $autoIncrement = '';
96

97
    /**
98
     * @var array
99
     */
100
    protected $columns = [];
101

102
    /**
103
     * @var array
104
     */
105
    protected $indexes = [];
106

107
    /**
108
     * @var string
109
     */
110
    protected $lastColumn;
111

112
    /**
113
     * @var bool
114
     */
115
    protected $isChange = false;
116

117
    /**
118
     * @var array
119
     */
120
    protected $columnDefaults = [
121
        'nullable' => false,
122
        'comment' => '',
123
        'unsigned' => false,
124
        'change' => false,
125
    ];
126

127
    /**
128
     * The default values for types, false means no default value
129
     *
130
     * @var array
131
     */
132
    protected $typeDefaults = [
133
        self::TYPE_BIG_INT => '0',
134
        self::TYPE_BOOL => '0',
135
        self::TYPE_CHAR => '',
136
        self::TYPE_DATE => null,
137
        self::TYPE_DATETIME => null,
138
        self::TYPE_DECIMAL => '0',
139
        self::TYPE_DOUBLE => '0',
140
        self::TYPE_INT => '0',
141
        self::TYPE_LONG_TEXT => false,
142
        self::TYPE_MEDIUM_INT => '0',
143
        self::TYPE_MEDIUM_TEXT => false,
144
        self::TYPE_TINY_INT => '0',
145
        self::TYPE_SMALL_INT => '0',
146
        self::TYPE_STRING => '',
147
        self::TYPE_TEXT => false,
148
        self::TYPE_TIMESTAMP => null,
149
        self::TYPE_JSON => false,
150
        self::TYPE_BINARY => '',
151
        self::TYPE_VAR_BINARY => '',
152
    ];
153

154
    /**
155
     * @var array
156
     */
157
    protected $typeMaps = [
158
        'mysql' => [
159
            self::TYPE_BIG_INT => 'bigint',
160
            self::TYPE_BOOL => 'tinyint(1)',
161
            self::TYPE_CHAR => 'char',
162
            self::TYPE_DATE => 'date',
163
            self::TYPE_DATETIME => 'datetime',
164
            self::TYPE_DECIMAL => 'decimal',
165
            self::TYPE_DOUBLE => 'double',
166
            self::TYPE_INT => 'int',
167
            self::TYPE_LONG_TEXT => 'longtext',
168
            self::TYPE_MEDIUM_INT => 'mediumint',
169
            self::TYPE_MEDIUM_TEXT => 'mediumtext',
170
            self::TYPE_TINY_INT => 'tinyint',
171
            self::TYPE_SMALL_INT => 'smallint',
172
            self::TYPE_STRING => 'varchar',
173
            self::TYPE_TEXT => 'text',
174
            self::TYPE_TIMESTAMP => 'timestamp',
175
            self::TYPE_JSON => 'json',
176
            self::TYPE_BINARY => 'binary',
177
            self::TYPE_VAR_BINARY => 'varbinary',
178
        ],
179
    ];
180

181
    /**
182
     * The SQL to check if table exists
183
     *
184
     * @var array
185
     */
186
    protected $checkTableSqls = [
187
        'mysql' => 'SELECT * FROM information_schema.tables WHERE table_schema = ? AND table_name = ? LIMIT 1;',
188
    ];
189

190
    /**
191
     * @var array
192
     */
193
    protected $stringTypes = [
194
        self::TYPE_CHAR,
195
        self::TYPE_STRING,
196
        self::TYPE_JSON,
197
    ];
198

199
    /**
200
     * @param string $table
201
     * @return $this
202
     */
203
    public function table($table)
204
    {
205
        $this->reset();
2,052✔
206
        $this->table = $table;
2,052✔
207

208
        return $this;
2,052✔
209
    }
210

211
    /**
212
     * @param string $column
213
     * @param string $type
214
     * @param array $options
215
     * @return $this
216
     */
217
    public function addColumn($column, $type, array $options = [])
218
    {
219
        $this->lastColumn = $column;
2,044✔
220
        $this->columns[$column] = ['type' => $type] + $options;
2,044✔
221

222
        return $this;
2,044✔
223
    }
224

225
    /**
226
     * Add the last column from a schema to current schema
227
     *
228
     * @param Schema $schema
229
     * @return $this
230
     * @experimental may be merge into `addColumn`, add `Column` class
231
     */
232
    public function add(self $schema): self
233
    {
234
        $this->columns[$schema->lastColumn] = $schema->columns[$schema->lastColumn];
8✔
235
        return $this;
8✔
236
    }
237

238
    /**
239
     * Add a drop column command
240
     *
241
     * @param string|array $column
242
     * @return $this
243
     */
244
    public function dropColumn($column)
245
    {
246
        $this->isChange = true;
16✔
247
        foreach ((array) $column as $item) {
16✔
248
            $this->columns[$item] = ['command' => 'drop'];
16✔
249
        }
250

251
        return $this;
16✔
252
    }
253

254
    /**
255
     * Set position for current column
256
     *
257
     * @param string $column
258
     * @return $this
259
     */
260
    public function after($column)
261
    {
262
        return $this->updateLastColumn('after', $column);
8✔
263
    }
264

265
    /**
266
     * @return $this
267
     */
268
    public function change()
269
    {
270
        $this->isChange = true;
8✔
271

272
        return $this->updateLastColumn('change', true);
8✔
273
    }
274

275
    /**
276
     * @param string $name
277
     * @param mixed $value
278
     * @return $this
279
     */
280
    public function updateLastColumn($name, $value)
281
    {
282
        $this->columns[$this->lastColumn][$name] = $value;
2,020✔
283

284
        return $this;
2,020✔
285
    }
286

287
    /**
288
     * Return create/change table sql
289
     *
290
     * @return string
291
     */
292
    public function getSql()
293
    {
294
        if ($this->hasTable($this->table)) {
2,052✔
295
            $this->isChange = true;
32✔
296
        }
297

298
        $table = $this->db->getTable($this->table);
2,052✔
299
        $columnSql = $this->getCreateDefinition();
2,052✔
300

301
        if ($this->isChange) {
2,052✔
302
            $sql = "ALTER TABLE $table" . rtrim($columnSql);
40✔
303
        } else {
304
            $sql = "CREATE TABLE $table ($columnSql)";
2,036✔
305
            $sql .= $this->getTableOptionSql();
2,036✔
306
        }
307

308
        $sql .= ';';
2,052✔
309

310
        return $sql;
2,052✔
311
    }
312

313
    /**
314
     * @param bool $defaultNullable
315
     * @return $this
316
     */
317
    public function setDefaultNullable($defaultNullable)
318
    {
319
        $this->defaultNullable = (bool) $defaultNullable;
8✔
320
        return $this;
8✔
321
    }
322

323
    /**
324
     * @return bool
325
     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
326
     */
327
    public function getDefaultNullable()
328
    {
329
        return $this->defaultNullable;
×
330
    }
331

332
    /**
333
     * @return bool
334
     */
335
    public function isDefaultNullable()
336
    {
337
        return $this->defaultNullable;
×
338
    }
339

340
    /**
341
     * Execute a drop table sql
342
     *
343
     * @param string|array $table
344
     * @param bool $ifExists
345
     * @return $this
346
     */
347
    public function drop($table, $ifExists = false)
348
    {
349
        foreach ((array) $table as $name) {
1,980✔
350
            $sql = 'DROP TABLE ';
1,980✔
351
            if ($ifExists) {
1,980✔
352
                $sql .= 'IF EXISTS ';
1,980✔
353
            }
354
            $sql .= $this->db->getTable($name);
1,980✔
355

356
            $this->db->executeUpdate($sql);
1,980✔
357
        }
358

359
        return $this;
1,980✔
360
    }
361

362
    /**
363
     * Execute a drop table if exist sql
364
     *
365
     * @param string|array $table
366
     * @return $this
367
     */
368
    public function dropIfExists($table)
369
    {
370
        return $this->drop($table, true);
1,980✔
371
    }
372

373
    /**
374
     * Check if table exists
375
     *
376
     * @param string $table
377
     * @return bool
378
     */
379
    public function hasTable($table)
380
    {
381
        $parts = explode('.', $table);
2,052✔
382
        if (1 === count($parts)) {
2,052✔
383
            $db = $this->db->getDbname();
2,052✔
384
            $table = $parts[0];
2,052✔
385
        } else {
386
            list($db, $table) = $parts;
×
387
        }
388
        $table = $this->db->getTable($table);
2,052✔
389

390
        $tableExistsSql = $this->checkTableSqls[$this->db->getDriver()];
2,052✔
391

392
        return (bool) $this->db->fetchColumn($tableExistsSql, [$db, $table]);
2,052✔
393
    }
394

395
    /**
396
     * Check if database exists
397
     *
398
     * @param string $database
399
     * @return bool
400
     * @svc
401
     */
402
    protected function hasDatabase(string $database): bool
403
    {
404
        return (bool) $this->db->fetchColumn('SHOW DATABASES LIKE ?', [$database]);
8✔
405
    }
406

407
    /**
408
     * Create a database
409
     *
410
     * @param string $database
411
     * @return $this
412
     * @svc
413
     */
414
    protected function createDatabase(string $database): self
415
    {
416
        $this->db->executeUpdate('CREATE DATABASE ' . $database);
8✔
417
        return $this;
8✔
418
    }
419

420
    /**
421
     * Drop a database
422
     *
423
     * @param string $database
424
     * @return $this
425
     * @svc
426
     */
427
    protected function dropDatabase(string $database): self
428
    {
429
        $this->db->executeUpdate('DROP DATABASE ' . $database);
8✔
430
        return $this;
8✔
431
    }
432

433
    /**
434
     * Set user id type
435
     *
436
     * @param string $userIdType
437
     * @return $this
438
     * @svc
439
     */
440
    protected function setUserIdType(string $userIdType): self
441
    {
442
        if ('u' === substr($userIdType, 0, 1)) {
128✔
443
            $type = lcfirst(substr($userIdType, 1));
128✔
444
        } else {
445
            $type = $userIdType;
8✔
446
        }
447

448
        if (!array_key_exists($type, $this->typeDefaults)) {
128✔
449
            throw new \InvalidArgumentException(sprintf('Invalid user id type "%s"', $userIdType));
8✔
450
        }
451

452
        $this->userIdType = $userIdType;
128✔
453
        return $this;
128✔
454
    }
455

456
    /**
457
     * Get user id type
458
     *
459
     * @return string
460
     * @svc
461
     */
462
    protected function getUserIdType(): string
463
    {
464
        return $this->userIdType;
8✔
465
    }
466

467
    /**
468
     * @return $this
469
     */
470
    public function exec()
471
    {
472
        $this->db->executeUpdate($this->getSql());
1,980✔
473

474
        return $this;
1,980✔
475
    }
476

477
    /**
478
     * Add a big int column
479
     *
480
     * @param string $column
481
     * @return $this
482
     */
483
    public function bigInt($column)
484
    {
485
        return $this->addColumn($column, self::TYPE_BIG_INT);
32✔
486
    }
487

488
    /**
489
     * Add an unsigned big int column
490
     *
491
     * @param string $column
492
     * @return $this
493
     */
494
    public function uBigInt($column)
495
    {
496
        return $this->bigInt($column)->unsigned();
32✔
497
    }
498

499
    /**
500
     * Add a char column
501
     *
502
     * @param string $column
503
     * @return $this
504
     */
505
    public function bool($column)
506
    {
507
        return $this->addColumn($column, self::TYPE_BOOL);
1,732✔
508
    }
509

510
    /**
511
     * Add a char column
512
     *
513
     * @param string $column
514
     * @param int $length
515
     * @return $this
516
     */
517
    public function char($column, $length = 255)
518
    {
519
        return $this->addColumn($column, self::TYPE_CHAR, ['length' => $length]);
8✔
520
    }
521

522
    /**
523
     * Add a decimal column
524
     *
525
     * @param string $column
526
     * @param int $length
527
     * @param int $scale
528
     * @return $this
529
     */
530
    public function decimal($column, $length = 10, $scale = 2)
531
    {
532
        return $this->addColumn($column, self::TYPE_DECIMAL, ['length' => $length, 'scale' => $scale]);
8✔
533
    }
534

535
    /**
536
     * Add an unsigned decimal column
537
     *
538
     * @param string $column
539
     * @param int $length
540
     * @param int $scale
541
     * @return $this
542
     */
543
    public function uDecimal($column, $length = 10, $scale = 2)
544
    {
545
        return $this->decimal($column, $length, $scale)->unsigned();
8✔
546
    }
547

548
    /**
549
     * Add a double column
550
     *
551
     * @param string $column
552
     * @return $this
553
     */
554
    public function double($column)
555
    {
556
        return $this->addColumn($column, self::TYPE_DOUBLE);
8✔
557
    }
558

559
    /**
560
     * Add an unsigned double column
561
     *
562
     * @param string $column
563
     * @return $this
564
     */
565
    public function uDouble($column)
566
    {
567
        return $this->double($column)->unsigned();
8✔
568
    }
569

570
    /**
571
     * Add a string(varchar) column
572
     *
573
     * @param string $column
574
     * @param int $length
575
     * @return $this
576
     */
577
    public function string($column, $length = 255)
578
    {
579
        return $this->addColumn($column, self::TYPE_STRING, ['length' => $length]);
2,004✔
580
    }
581

582
    public function binary(string $column, int $length): self
583
    {
584
        return $this->addColumn($column, self::TYPE_BINARY, ['length' => $length]);
×
585
    }
586

587
    public function varBinary(string $column, int $length): self
588
    {
589
        return $this->addColumn($column, self::TYPE_VAR_BINARY, ['length' => $length]);
×
590
    }
591

592
    /**
593
     * Add a int column
594
     *
595
     * @param string $column
596
     * @param int|null $length
597
     * @return $this
598
     */
599
    public function int($column, $length = null)
600
    {
601
        return $this->addColumn($column, self::TYPE_INT, ['length' => $length]);
1,980✔
602
    }
603

604
    /**
605
     * Add an unsigned int column
606
     *
607
     * @param string $column
608
     * @param int|null $length
609
     * @return $this
610
     */
611
    public function uInt($column, $length = null)
612
    {
613
        return $this->int($column, $length)->unsigned();
1,980✔
614
    }
615

616
    /**
617
     * Add a long text column
618
     *
619
     * @param $column
620
     * @return $this
621
     */
622
    public function longText($column)
623
    {
624
        return $this->addColumn($column, self::TYPE_LONG_TEXT);
8✔
625
    }
626

627
    /**
628
     * Add a medium int column
629
     *
630
     * @param $column
631
     * @return $this
632
     */
633
    public function mediumInt($column)
634
    {
635
        return $this->addColumn($column, self::TYPE_MEDIUM_INT);
8✔
636
    }
637

638
    /**
639
     * Add an unsigned medium int column
640
     *
641
     * @param $column
642
     * @return $this
643
     */
644
    public function uMediumInt($column)
645
    {
646
        return $this->mediumInt($column)->unsigned();
8✔
647
    }
648

649
    /**
650
     * Add a medium text column
651
     *
652
     * @param $column
653
     * @return $this
654
     */
655
    public function mediumText($column)
656
    {
657
        return $this->addColumn($column, self::TYPE_MEDIUM_TEXT);
8✔
658
    }
659

660
    /**
661
     * Add a tiny int column
662
     *
663
     * @param $column
664
     * @param int|null $length
665
     * @return $this
666
     */
667
    public function tinyInt($column, $length = null)
668
    {
669
        return $this->addColumn($column, self::TYPE_TINY_INT, ['length' => $length]);
8✔
670
    }
671

672
    /**
673
     * Add an unsigned tiny int column
674
     *
675
     * @param $column
676
     * @param int|null $length
677
     * @return $this
678
     */
679
    public function uTinyInt($column, $length = null)
680
    {
681
        return $this->tinyInt($column, $length)->unsigned();
8✔
682
    }
683

684
    /**
685
     * Add a small int column
686
     *
687
     * @param $column
688
     * @param int|null $length
689
     * @return $this
690
     */
691
    public function smallInt($column, $length = null)
692
    {
693
        return $this->addColumn($column, self::TYPE_SMALL_INT, ['length' => $length]);
8✔
694
    }
695

696
    /**
697
     * Add an unsigned small int column
698
     *
699
     * @param $column
700
     * @param int|null $length
701
     * @return $this
702
     */
703
    public function uSmallInt($column, $length = null)
704
    {
705
        return $this->smallInt($column, $length)->unsigned();
8✔
706
    }
707

708
    /**
709
     * Add a text column
710
     *
711
     * @param $column
712
     * @return $this
713
     */
714
    public function text($column)
715
    {
716
        return $this->addColumn($column, self::TYPE_TEXT);
40✔
717
    }
718

719
    /**
720
     * Add a date column
721
     *
722
     * @param string $column
723
     * @return $this
724
     */
725
    public function date($column)
726
    {
727
        return $this->addColumn($column, self::TYPE_DATE);
1,732✔
728
    }
729

730
    /**
731
     * Add a datetime column
732
     *
733
     * @param string $column
734
     * @return $this
735
     */
736
    public function datetime($column)
737
    {
738
        return $this->addColumn($column, self::TYPE_DATETIME);
8✔
739
    }
740

741
    /**
742
     * Add a timestamp column
743
     *
744
     * @param string $column
745
     * @return $this
746
     */
747
    public function timestamp($column)
748
    {
749
        return $this->addColumn($column, self::TYPE_TIMESTAMP);
24✔
750
    }
751

752
    /**
753
     * Add a timestamp column
754
     *
755
     * @param string $column
756
     * @return $this
757
     */
758
    public function json($column)
759
    {
760
        return $this->addColumn($column, self::TYPE_JSON);
8✔
761
    }
762

763
    /**
764
     * Set comment for current column
765
     *
766
     * @param string $comment
767
     * @return $this
768
     */
769
    public function comment($comment)
770
    {
771
        return $this->updateLastColumn('comment', $comment);
56✔
772
    }
773

774
    /**
775
     * Set default value for current column
776
     *
777
     * @param mixed $value
778
     * @return $this
779
     */
780
    public function defaults($value)
781
    {
782
        return $this->updateLastColumn('default', $value);
1,732✔
783
    }
784

785
    /**
786
     * Set nullable attribute for current column
787
     *
788
     * @param bool $nullable
789
     * @return $this
790
     */
791
    public function nullable($nullable = true)
792
    {
793
        return $this->updateLastColumn('nullable', $nullable);
1,732✔
794
    }
795

796
    /**
797
     * Set unsigned attribute for current column
798
     *
799
     * @param bool $unsigned
800
     * @return $this
801
     */
802
    public function unsigned($unsigned = true)
803
    {
804
        return $this->updateLastColumn('unsigned', $unsigned);
2,004✔
805
    }
806

807
    /**
808
     *  Add a unique index to specified column
809
     *
810
     * @param string|array $columns
811
     * @param string|null $name
812
     * @return $this
813
     */
814
    public function unique($columns, $name = null)
815
    {
816
        return $this->addIndex($columns, $name, __FUNCTION__);
8✔
817
    }
818

819
    /**
820
     * Add a primary index to specified column
821
     *
822
     * @param string|array $columns
823
     * @param string|null $name
824
     * @return $this
825
     */
826
    public function primary($columns, $name = null)
827
    {
828
        return $this->addIndex($columns, $name, __FUNCTION__);
1,988✔
829
    }
830

831
    /**
832
     * Add a index to specified column
833
     *
834
     * @param string|array $columns
835
     * @param string|null $name
836
     * @return $this
837
     */
838
    public function index($columns, $name = null)
839
    {
840
        return $this->addIndex($columns, $name, __FUNCTION__);
8✔
841
    }
842

843
    /**
844
     * Add a drop index command
845
     *
846
     * @param string|array $index
847
     * @return $this
848
     */
849
    public function dropIndex($index)
850
    {
851
        $this->isChange = true;
×
852
        foreach ((array) $index as $item) {
×
853
            $this->indexes[$item] = ['command' => 'drop'];
×
854
        }
855

856
        return $this;
×
857
    }
858

859
    /**
860
     * Set current column is auto increment
861
     *
862
     * @return $this
863
     */
864
    public function autoIncrement()
865
    {
866
        $this->autoIncrement = $this->lastColumn;
1,988✔
867

868
        return $this;
1,988✔
869
    }
870

871
    /**
872
     * Add a int auto increment id to table
873
     *
874
     * @param string $column
875
     * @return $this
876
     */
877
    public function id($column = 'id')
878
    {
879
        $this->uInt($column)->autoIncrement();
1,980✔
880

881
        return $this->primary($column);
1,980✔
882
    }
883

884
    /**
885
     * Add a big int auto increment id to table
886
     *
887
     * @param string $column
888
     * @return $this
889
     */
890
    public function bigId($column = 'id')
891
    {
892
        $this->uBigInt($column)->autoIncrement();
8✔
893

894
        return $this->primary($column);
8✔
895
    }
896

897
    /**
898
     * Add created_at and updated_at columns to current table
899
     *
900
     * @return $this
901
     */
902
    public function timestamps()
903
    {
904
        return $this->timestamp('created_at')->timestamp('updated_at');
8✔
905
    }
906

907
    /**
908
     * Add created_by and updated_by columns to current table
909
     *
910
     * @return $this
911
     */
912
    public function userstamps()
913
    {
914
        return $this->{$this->userIdType}('created_by')->{$this->userIdType}('updated_by');
16✔
915
    }
916

917
    /**
918
     * Add deleted_at and deleted_by columns to current table
919
     *
920
     * @return $this
921
     */
922
    public function softDeletable()
923
    {
924
        return $this->timestamp('deleted_at')->{$this->userIdType}('deleted_by');
16✔
925
    }
926

927
    /**
928
     * Remove deleted_at and deleted_by columns in current table
929
     *
930
     * @return $this
931
     */
932
    public function dropSoftDeletable(): self
933
    {
934
        return $this->dropColumn(['deleted_at', 'deleted_by']);
×
935
    }
936

937
    /**
938
     * Add a user id column to table
939
     *
940
     * @param string $column
941
     * @return $this
942
     */
943
    public function userId(string $column = 'user_id'): self
944
    {
945
        return $this->{$this->userIdType}($column);
×
946
    }
947

948
    /**
949
     * Set the comment for current table
950
     *
951
     * @param string $comment
952
     * @return $this
953
     */
954
    public function tableComment($comment)
955
    {
956
        $this->tableComment = $comment;
8✔
957

958
        return $this;
8✔
959
    }
960

961
    /**
962
     * Add a rename column command
963
     *
964
     * @param string $from
965
     * @param string $to
966
     * @return $this
967
     */
968
    public function renameColumn($from, $to)
969
    {
970
        $this->columns[$from] = ['command' => 'rename', 'from' => $from, 'to' => $to];
32✔
971

972
        return $this;
32✔
973
    }
974

975
    /**
976
     * Execute a rename table sql
977
     *
978
     * @param string $from
979
     * @param string $to
980
     * @return $this
981
     */
982
    public function rename($from, $to)
983
    {
984
        $sql = sprintf('RENAME TABLE %s TO %s', $this->db->getTable($from), $this->db->getTable($to));
8✔
985
        $this->db->executeUpdate($sql);
8✔
986

987
        return $this;
8✔
988
    }
989

990
    protected function reset()
991
    {
992
        $this->table = '';
2,052✔
993
        $this->columns = [];
2,052✔
994
        $this->indexes = [];
2,052✔
995
        $this->lastColumn = null;
2,052✔
996
        $this->autoIncrement = '';
2,052✔
997
        $this->tableComment = '';
2,052✔
998
        $this->isChange = false;
2,052✔
999
    }
255✔
1000

1001
    /**
1002
     * @return string
1003
     */
1004
    protected function getCreateDefinition()
1005
    {
1006
        $columnSqls = [];
2,052✔
1007
        foreach ($this->columns as $column => $options) {
2,052✔
1008
            $columnSqls[] = '  ' . $this->getColumnSql($column, $options);
2,052✔
1009
        }
1010

1011
        foreach ($this->indexes as $index => $options) {
2,052✔
1012
            $columnSqls[] = ' ' . $this->getIndexSql($index, $options);
1,988✔
1013
        }
1014

1015
        $sql = "\n" . implode(",\n", $columnSqls) . "\n";
2,052✔
1016

1017
        return $sql;
2,052✔
1018
    }
1019

1020
    /**
1021
     * @param string $index
1022
     * @param array $options
1023
     * @return string
1024
     */
1025
    protected function getIndexSql($index, array $options)
1026
    {
1027
        $sql = ' ';
1,988✔
1028

1029
        if (isset($options['command'])) {
1,988✔
1030
            $method = 'get' . ucfirst($options['command']) . 'IndexSql';
×
1031

1032
            return $this->{$method}($index, $options);
×
1033
        }
1034

1035
        if ('index' !== $options['type']) {
1,988✔
1036
            $sql .= strtoupper($options['type']) . ' ';
1,988✔
1037
        }
1038

1039
        if ($this->isChange) {
1,988✔
1040
            $sql .= 'ADD ';
×
1041
        }
1042

1043
        $sql .= 'KEY ' . $index . ' (' . implode(', ', $options['columns']) . ')';
1,988✔
1044

1045
        return $sql;
1,988✔
1046
    }
1047

1048
    /**
1049
     * @return string
1050
     */
1051
    protected function getTableOptionSql()
1052
    {
1053
        $sql = '';
2,036✔
1054
        if ($this->engine) {
2,036✔
1055
            $sql .= ' ENGINE=' . $this->engine;
2,036✔
1056
        }
1057

1058
        if ($this->charset) {
2,036✔
1059
            $sql .= ' CHARSET=' . $this->charset;
2,036✔
1060
        }
1061

1062
        if ($this->collate) {
2,036✔
1063
            $sql .= ' COLLATE=' . $this->collate;
2,036✔
1064
        }
1065

1066
        if ($this->tableComment) {
2,036✔
1067
            $sql .= " COMMENT='" . $this->tableComment . "'";
8✔
1068
        }
1069

1070
        return $sql;
2,036✔
1071
    }
1072

1073
    /**
1074
     * @param string $column
1075
     * @param array $options
1076
     * @return string
1077
     */
1078
    protected function getColumnSql($column, array $options)
1079
    {
1080
        $sql = '';
2,052✔
1081

1082
        if (isset($options['command'])) {
2,052✔
1083
            $method = 'get' . ucfirst($options['command']) . 'ColumnSql';
40✔
1084

1085
            return $this->{$method}($column, $options);
40✔
1086
        }
1087

1088
        if ($this->isChange) {
2,044✔
1089
            if (isset($options['change'])) {
16✔
1090
                $sql .= $this->getChangeColumnSql($column);
8✔
1091
            } else {
1092
                $sql .= 'ADD COLUMN ';
16✔
1093
            }
1094
        }
1095

1096
        return $sql . $this->buildColumnSql($column, $options);
2,044✔
1097
    }
1098

1099
    /**
1100
     * @param string $column
1101
     * @param array $options
1102
     * @return string
1103
     */
1104
    protected function buildColumnSql($column, array $options)
1105
    {
1106
        $sql = $column . ' ' . $this->getTypeSql($options) . ' ';
2,044✔
1107
        $sql .= $this->getUnsignedSql($options);
2,044✔
1108

1109
        // Avoid automatic generate "NOT NULL DEFAULT NULL" error statement, convert it to "NULL DEFAULT NULL"
1110
        if (
1111
            !array_key_exists('default', $options)
2,044✔
1112
            && $this->autoDefault
2,044✔
1113
            && null === $this->typeDefaults[$options['type']]
2,044✔
1114
            && !$this->defaultNullable
2,044✔
1115
        ) {
1116
            $options['nullable'] = true;
1,748✔
1117
        }
1118

1119
        $sql .= $this->getNullSql(isset($options['nullable']) ? $options['nullable'] : $this->defaultNullable);
2,044✔
1120

1121
        // Auto increment do not have default value
1122
        if ($this->autoIncrement == $column) {
2,044✔
1123
            $sql .= ' AUTO_INCREMENT';
1,988✔
1124
        } else {
1125
            $defaultSql = $this->getDefaultSql($options);
2,028✔
1126
            if ($defaultSql) {
2,028✔
1127
                $sql .= ' ' . $defaultSql;
2,028✔
1128
            }
1129
        }
1130

1131
        if (isset($options['comment'])) {
2,044✔
1132
            $sql .= " COMMENT '" . $options['comment'] . "'";
56✔
1133
        }
1134

1135
        if (isset($options['after'])) {
2,044✔
1136
            $sql .= ' AFTER ' . $options['after'];
8✔
1137
        }
1138

1139
        return $sql;
2,044✔
1140
    }
1141

1142
    /**
1143
     * @param string $column
1144
     * @return string
1145
     */
1146
    protected function getChangeColumnSql($column)
1147
    {
1148
        return 'CHANGE COLUMN ' . $column . ' ';
32✔
1149
    }
1150

1151
    /**
1152
     * @param string $column
1153
     * @return string
1154
     */
1155
    protected function getDropColumnSql($column)
1156
    {
1157
        return 'DROP COLUMN ' . $column;
16✔
1158
    }
1159

1160
    /**
1161
     * @param string $column
1162
     * @param array $options
1163
     * @return string
1164
     * @throws \Exception when column not found in table
1165
     */
1166
    protected function getRenameColumnSql($column, $options)
1167
    {
1168
        $table = $this->db->getTable($this->table);
32✔
1169
        $dbColumns = $this->db->fetchAll("SHOW FULL COLUMNS FROM $table");
32✔
1170
        $fromColumn = null;
32✔
1171
        foreach ($dbColumns as $dbColumn) {
32✔
1172
            if ($dbColumn['Field'] == $options['from']) {
32✔
1173
                $fromColumn = $dbColumn;
24✔
1174
                break;
24✔
1175
            }
1176
        }
1177
        if (!$fromColumn) {
32✔
1178
            throw new \Exception(sprintf('Column "%s" not found in table "%s"', $options['from'], $this->table));
8✔
1179
        }
1180

1181
        $newOptions = [];
24✔
1182
        $newOptions['type'] = $fromColumn['Type'];
24✔
1183
        $newOptions['nullable'] = 'YES' === $fromColumn['Null'];
24✔
1184
        $newOptions['comment'] = $fromColumn['Comment'];
24✔
1185

1186
        $keys = array_keys($this->typeDefaults, false, true);
24✔
1187
        if (!in_array($newOptions['type'], $keys, true)) {
24✔
1188
            $newOptions['default'] = $fromColumn['Default'];
16✔
1189
        }
1190

1191
        $sql = $this->buildColumnSql($options['to'], $newOptions);
24✔
1192
        if ('auto_increment' === $fromColumn['Extra']) {
24✔
1193
            $sql .= ' AUTO_INCREMENT';
×
1194
        }
1195

1196
        return $this->getChangeColumnSql($column) . $sql;
24✔
1197
    }
1198

1199
    /**
1200
     * @param string $index
1201
     * @return string
1202
     */
1203
    protected function getDropIndexSql($index)
1204
    {
1205
        return 'DROP INDEX ' . $index;
×
1206
    }
1207

1208
    /**
1209
     * @param array $options
1210
     * @return string
1211
     */
1212
    protected function getTypeSql(array $options)
1213
    {
1214
        $driver = $this->db->getDriver();
2,044✔
1215
        $typeMap = $this->typeMaps[$driver];
2,044✔
1216

1217
        // Allow custom type (eg int(10) unsigned) from rename
1218
        if (!isset($typeMap[$options['type']])) {
2,044✔
1219
            return $options['type'];
16✔
1220
        }
1221

1222
        $sql = $typeMap[$options['type']];
2,044✔
1223

1224
        if (isset($options['length'])) {
2,044✔
1225
            if (isset($options['scale'])) {
2,004✔
1226
                $sql .= '(' . $options['length'] . ', ' . $options['scale'] . ')';
8✔
1227
            } else {
1228
                $sql .= '(' . $options['length'] . ')';
2,004✔
1229
            }
1230
        }
1231

1232
        return $sql;
2,044✔
1233
    }
1234

1235
    /**
1236
     * @param array $options
1237
     * @return string
1238
     */
1239
    protected function getUnsignedSql(array $options)
1240
    {
1241
        if (isset($options['unsigned'])) {
2,044✔
1242
            return $options['unsigned'] ? 'unsigned ' : '';
2,004✔
1243
        }
1244
        return '';
2,020✔
1245
    }
1246

1247
    /**
1248
     * @param bool $null
1249
     * @return string
1250
     */
1251
    protected function getNullSql($null)
1252
    {
1253
        return $null ? 'NULL' : 'NOT NULL';
2,044✔
1254
    }
1255

1256
    /**
1257
     * @param array $options
1258
     * @return string
1259
     */
1260
    protected function getDefaultSql(array $options)
1261
    {
1262
        $hasDefault = array_key_exists('default', $options);
2,028✔
1263
        if (!$hasDefault && !$this->autoDefault) {
2,028✔
1264
            return '';
×
1265
        }
1266

1267
        if (!$hasDefault && $this->autoDefault) {
2,028✔
1268
            $options['default'] = $this->typeDefaults[$options['type']];
2,028✔
1269
        }
1270

1271
        $default = $options['default'];
2,028✔
1272
        if (false === $default) {
2,028✔
1273
            return '';
40✔
1274
        }
1275

1276
        switch (true) {
1277
            case '' === $default:
2,028✔
1278
                $value = "''";
2,004✔
1279
                break;
2,004✔
1280

1281
            case in_array($options['type'], $this->stringTypes, true):
1,756✔
1282
                $value = var_export($default, true);
1,724✔
1283
                break;
1,724✔
1284

1285
            case null === $default:
1,756✔
1286
                $value = 'NULL';
1,748✔
1287
                break;
1,748✔
1288

1289
            case is_bool($default):
1,748✔
1290
                $value = (string) $default;
8✔
1291
                break;
8✔
1292

1293
            default:
1294
                $value = $default;
1,748✔
1295
        }
1296

1297
        return 'DEFAULT ' . $value;
2,028✔
1298
    }
1299

1300
    /**
1301
     * @param string|array $columns
1302
     * @param string $name
1303
     * @param string $type
1304
     * @return $this
1305
     */
1306
    protected function addIndex($columns, $name, $type)
1307
    {
1308
        $columns = (array) $columns;
1,988✔
1309
        $name || $name = $this->generateIndexName($columns);
1,988✔
1310

1311
        $this->indexes[$name] = [
1,988✔
1312
            'columns' => $columns,
1,988✔
1313
            'type' => $type,
1,988✔
1314
        ];
1,741✔
1315

1316
        return $this;
1,988✔
1317
    }
1318

1319
    /**
1320
     * @param array $columns
1321
     * @return string
1322
     */
1323
    protected function generateIndexName(array $columns)
1324
    {
1325
        return implode('_', $columns);
1,988✔
1326
    }
1327
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc