-
Notifications
You must be signed in to change notification settings - Fork 892
/
migrations.rst
1934 lines (1465 loc) · 57.3 KB
/
migrations.rst
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
.. index::
single: Writing Migrations
Writing Migrations
==================
Phinx relies on migrations in order to transform your database. Each migration
is represented by a PHP class in a unique file. It is preferred that you write
your migrations using the Phinx PHP API, but raw SQL is also supported.
Creating a New Migration
------------------------
Generating a skeleton migration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Let's start by creating a new Phinx migration. Run Phinx using the ``create``
command:
.. code-block:: bash
$ vendor/bin/phinx create MyNewMigration
This will create a new migration in the format
``YYYYMMDDHHMMSS_my_new_migration.php``, where the first 14 characters are
replaced with the current timestamp down to the second.
If you have specified multiple migration paths, you will be asked to select
which path to create the new migration in.
Phinx automatically creates a skeleton migration file with a single method:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*
*/
public function change()
{
}
}
All Phinx migrations extend from the ``AbstractMigration`` class. This class
provides the necessary support to create your database migrations. Database
migrations can transform your database in many ways, such as creating new
tables, inserting rows, adding indexes and modifying columns.
The Change Method
~~~~~~~~~~~~~~~~~
Phinx 0.2.0 introduced a new feature called reversible migrations. This feature
has now become the default migration method. With reversible migrations, you
only need to define the ``up`` logic, and Phinx can figure out how to migrate
down automatically for you. For example:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class CreateUserLoginsTable extends AbstractMigration
{
public function change()
{
// create the table
$table = $this->table('user_logins');
$table->addColumn('user_id', 'integer')
->addColumn('created', 'datetime')
->create();
}
}
When executing this migration, Phinx will create the ``user_logins`` table on
the way up and automatically figure out how to drop the table on the way down.
Please be aware that when a ``change`` method exists, Phinx will automatically
ignore the ``up`` and ``down`` methods. If you need to use these methods it is
recommended to create a separate migration file.
.. note::
When creating or updating tables inside a ``change()`` method you must use
the Table ``create()`` and ``update()`` methods. Phinx cannot automatically
determine whether a ``save()`` call is creating a new table or modifying an
existing one.
The following actions are reversible when done through the Table API in Phinx,
and will be automatically reversed:
- Creating a table
- Renaming a table
- Adding a column
- Renaming a column
- Adding an index
- Adding a foreign key
If a command cannot be reversed then Phinx will throw an
``IrreversibleMigrationException`` when it's migrating down. If you wish to
use a command that cannot be reversed in the change function, you can use an
if statement with ``$this->isMigratingUp()`` to only run things in the
up or down direction. For example:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class CreateUserLoginsTable extends AbstractMigration
{
public function change()
{
// create the table
$table = $this->table('user_logins');
$table->addColumn('user_id', 'integer')
->addColumn('created', 'datetime')
->create();
if ($this->isMigratingUp()) {
$table->insert([['user_id' => 1, 'created' => '2020-01-19 03:14:07']])
->save();
}
}
}
The Up Method
~~~~~~~~~~~~~
The up method is automatically run by Phinx when you are migrating up and it
detects the given migration hasn't been executed previously. You should use the
up method to transform the database with your intended changes.
The Down Method
~~~~~~~~~~~~~~~
The down method is automatically run by Phinx when you are migrating down and
it detects the given migration has been executed in the past. You should use
the down method to reverse/undo the transformations described in the up method.
The Init Method
~~~~~~~~~~~~~~~
The ``init()`` method is run by Phinx before the migration methods if it exists.
This can be used for setting common class properties that are then used within
the migration methods.
The Should Execute Method
~~~~~~~~~~~~~~~~~~~~~~~~~
The ``shouldExecute()`` method is run by Phinx before executing the migration.
This can be used to prevent the migration from being executed at this time. It always
returns true by default. You can override it in your custom ``AbstractMigration``
implementation.
Executing Queries
-----------------
Queries can be executed with the ``execute()`` and ``query()`` methods. The
``execute()`` method returns the number of affected rows whereas the
``query()`` method returns the result as a
`PDOStatement <https://php.net/manual/en/class.pdostatement.php>`_. Both methods
accept an optional second parameter ``$params`` which is an array of elements,
and if used will cause the underlying connection to use a prepared statement.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// execute()
$count = $this->execute('DELETE FROM users'); // returns the number of affected rows
// query()
$stmt = $this->query('SELECT * FROM users'); // returns PDOStatement
$rows = $stmt->fetchAll(); // returns the result as an array
// using prepared queries
$count = $this->execute('DELETE FROM users WHERE id = ?', [5]);
$stmt = $this->query('SELECT * FROM users WHERE id > ?', [5]); // returns PDOStatement
$rows = $stmt->fetchAll();
}
/**
* Migrate Down.
*/
public function down()
{
}
}
.. note::
These commands run using the PHP Data Objects (PDO) extension which
defines a lightweight, consistent interface for accessing databases
in PHP. Always make sure your queries abide with PDOs before using
the ``execute()`` command. This is especially important when using
DELIMITERs during insertion of stored procedures or triggers which
don't support DELIMITERs.
.. note::
If you wish to execute multiple queries at once, you may not also use the prepared
variant of these functions. When using prepared queries, PDO can only execute
them one at a time.
.. warning::
When using ``execute()`` or ``query()`` with a batch of queries, PDO doesn't
throw an exception if there is an issue with one or more of the queries
in the batch.
As such, the entire batch is assumed to have passed without issue.
If Phinx was to iterate any potential result sets, looking to see if one
had an error, then Phinx would be denying access to all the results as there
is no facility in PDO to get a previous result set
`nextRowset() <https://php.net/manual/en/pdostatement.nextrowset.php>`_ -
but no ``previousSet()``).
So, as a consequence, due to the design decision in PDO to not throw
an exception for batched queries, Phinx is unable to provide the fullest
support for error handling when batches of queries are supplied.
Fortunately though, all the features of PDO are available, so multiple batches
can be controlled within the migration by calling upon
`nextRowset() <https://php.net/manual/en/pdostatement.nextrowset.php>`_
and examining `errorInfo <https://php.net/manual/en/pdostatement.errorinfo.php>`_.
Fetching Rows
-------------
There are two methods available to fetch rows. The ``fetchRow()`` method will
fetch a single row, whilst the ``fetchAll()`` method will return multiple rows.
Both methods accept raw SQL as their only parameter.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
// fetch a user
$row = $this->fetchRow('SELECT * FROM users');
// fetch an array of messages
$rows = $this->fetchAll('SELECT * FROM messages');
}
/**
* Migrate Down.
*/
public function down()
{
}
}
Inserting Data
--------------
Phinx makes it easy to insert data into your tables. Whilst this feature is
intended for the :doc:`seed feature <seeding>`, you are also free to use the
insert methods in your migrations.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class NewStatus extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$table = $this->table('status');
// inserting only one row
$singleRow = [
'id' => 1,
'name' => 'In Progress'
];
$table->insert($singleRow)->saveData();
// inserting multiple rows
$rows = [
[
'id' => 2,
'name' => 'Stopped'
],
[
'id' => 3,
'name' => 'Queued'
]
];
$table->insert($rows)->saveData();
}
/**
* Migrate Down.
*/
public function down()
{
$this->execute('DELETE FROM status');
}
}
.. note::
You cannot use the insert methods inside a `change()` method. Please use the
`up()` and `down()` methods.
Working With Tables
-------------------
The Table Object
~~~~~~~~~~~~~~~~
The Table object is one of the most useful APIs provided by Phinx. It allows
you to easily manipulate database tables using PHP code. You can retrieve an
instance of the Table object by calling the ``table()`` method from within
your database migration.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$table = $this->table('tableName');
}
/**
* Migrate Down.
*/
public function down()
{
}
}
You can then manipulate this table using the methods provided by the Table
object.
Saving Changes
~~~~~~~~~~~~~~
When working with the Table object, Phinx stores certain operations in a
pending changes cache. Once you have made the changes you want to the table,
you must save them. To perform this operation, Phinx provides three methods,
``create()``, ``update()``, and ``save()``. ``create()`` will first create
the table and then run the pending changes. ``update()`` will just run the
pending changes, and should be used when the table already exists. ``save()``
is a helper function that checks first if the table exists and if it does not
will run ``create()``, else it will run ``update()``.
As stated above, when using the ``change()`` migration method, you should always
use ``create()`` or ``update()``, and never ``save()`` as otherwise migrating
and rolling back may result in different states, due to ``save()`` calling
``create()`` when running migrate and then ``update()`` on rollback. When
using the ``up()``/``down()`` methods, it is safe to use either ``save()`` or
the more explicit methods.
When in doubt with working with tables, it is always recommended to call
the appropriate function and commit any pending changes to the database.
Creating a Table
~~~~~~~~~~~~~~~~
Creating a table is really easy using the Table object. Let's create a table to
store a collection of users.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
public function change()
{
$users = $this->table('users');
$users->addColumn('username', 'string', ['limit' => 20])
->addColumn('password', 'string', ['limit' => 40])
->addColumn('password_salt', 'string', ['limit' => 40])
->addColumn('email', 'string', ['limit' => 100])
->addColumn('first_name', 'string', ['limit' => 30])
->addColumn('last_name', 'string', ['limit' => 30])
->addColumn('created', 'datetime')
->addColumn('updated', 'datetime', ['null' => true])
->addIndex(['username', 'email'], ['unique' => true])
->create();
}
}
Columns are added using the ``addColumn()`` method. We create a unique index
for both the username and email columns using the ``addIndex()`` method.
Finally calling ``create()`` commits the changes to the database.
.. note::
Phinx automatically creates an auto-incrementing primary key column called ``id`` for every
table.
The ``id`` option sets the name of the automatically created identity field, while the ``primary_key``
option selects the field or fields used for primary key. ``id`` will always override the ``primary_key``
option unless it's set to false. If you don't need a primary key set ``id`` to false without
specifying a ``primary_key``, and no primary key will be created.
To specify an alternate primary key, you can specify the ``primary_key`` option
when accessing the Table object. Let's disable the automatic ``id`` column and
create a primary key using two columns instead:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
public function change()
{
$table = $this->table('followers', ['id' => false, 'primary_key' => ['user_id', 'follower_id']]);
$table->addColumn('user_id', 'integer')
->addColumn('follower_id', 'integer')
->addColumn('created', 'datetime')
->create();
}
}
Setting a single ``primary_key`` doesn't enable the ``AUTO_INCREMENT`` option.
To simply change the name of the primary key, we need to override the default ``id`` field name:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
public function up()
{
$table = $this->table('followers', ['id' => 'user_id']);
$table->addColumn('follower_id', 'integer')
->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->create();
}
}
In addition, the MySQL adapter supports following options:
========== ===========
Option Description
========== ===========
comment set a text comment on the table
row_format set the table row format
engine define table engine *(defaults to ``InnoDB``)*
collation define table collation *(defaults to ``utf8mb4_unicode_ci``)*
signed whether the primary key is ``signed`` *(defaults to ``false``)*
limit set the maximum length for the primary key
========== ===========
By default, the primary key is ``unsigned``.
To simply set it to be signed just pass ``signed`` option with a ``true`` value:
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
public function change()
{
$table = $this->table('followers', ['signed' => false]);
$table->addColumn('follower_id', 'integer')
->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->create();
}
}
The PostgreSQL adapter supports the following options:
========= ===========
Option Description
========= ===========
comment set a text comment on the table
========= ===========
To view available column types and options, see `Valid Column Types`_ for details.
Determining Whether a Table Exists
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can determine whether or not a table exists by using the ``hasTable()``
method.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$exists = $this->hasTable('users');
if ($exists) {
// do something
}
}
/**
* Migrate Down.
*/
public function down()
{
}
}
Dropping a Table
~~~~~~~~~~~~~~~~
Tables can be dropped quite easily using the ``drop()`` method. It is a
good idea to recreate the table again in the ``down()`` method.
Note that like other methods in the ``Table`` class, ``drop`` also needs ``save()``
to be called at the end in order to be executed. This allows phinx to intelligently
plan migrations when more than one table is involved.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$this->table('users')->drop()->save();
}
/**
* Migrate Down.
*/
public function down()
{
$users = $this->table('users');
$users->addColumn('username', 'string', ['limit' => 20])
->addColumn('password', 'string', ['limit' => 40])
->addColumn('password_salt', 'string', ['limit' => 40])
->addColumn('email', 'string', ['limit' => 100])
->addColumn('first_name', 'string', ['limit' => 30])
->addColumn('last_name', 'string', ['limit' => 30])
->addColumn('created', 'datetime')
->addColumn('updated', 'datetime', ['null' => true])
->addIndex(['username', 'email'], ['unique' => true])
->save();
}
}
Renaming a Table
~~~~~~~~~~~~~~~~
To rename a table access an instance of the Table object then call the
``rename()`` method.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$table = $this->table('users');
$table
->rename('legacy_users')
->update();
}
/**
* Migrate Down.
*/
public function down()
{
$table = $this->table('legacy_users');
$table
->rename('users')
->update();
}
}
Changing the Primary Key
~~~~~~~~~~~~~~~~~~~~~~~~
To change the primary key on an existing table, use the ``changePrimaryKey()`` method.
Pass in a column name or array of columns names to include in the primary key, or ``null`` to drop the primary key.
Note that the mentioned columns must be added to the table, they will not be added implicitly.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$users = $this->table('users');
$users
->addColumn('username', 'string', ['limit' => 20, 'null' => false])
->addColumn('password', 'string', ['limit' => 40])
->save();
$users
->addColumn('new_id', 'integer', ['null' => false])
->changePrimaryKey(['new_id', 'username'])
->save();
}
/**
* Migrate Down.
*/
public function down()
{
}
}
Changing the Table Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~
To change the comment on an existing table, use the ``changeComment()`` method.
Pass in a string to set as the new table comment, or ``null`` to drop the existing comment.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Up.
*/
public function up()
{
$users = $this->table('users');
$users
->addColumn('username', 'string', ['limit' => 20])
->addColumn('password', 'string', ['limit' => 40])
->save();
$users
->changeComment('This is the table with users auth information, password should be encrypted')
->save();
}
/**
* Migrate Down.
*/
public function down()
{
}
}
Working With Columns
--------------------
.. _valid-column-types:
Valid Column Types
~~~~~~~~~~~~~~~~~~
Column types are specified as strings and can be one of:
- binary
- boolean
- char
- date
- datetime
- decimal
- float
- double
- smallinteger
- integer
- biginteger
- string
- text
- time
- timestamp
- uuid
In addition, the MySQL adapter supports ``enum``, ``set``, ``blob``, ``tinyblob``, ``mediumblob``, ``longblob``, ``bit`` and ``json`` column types
(``json`` in MySQL 5.7 and above). When providing a limit value and using ``binary``, ``varbinary`` or ``blob`` and its subtypes, the retained column
type will be based on required length (see `Limit Option and MySQL`_ for details);
In addition, the Postgres adapter supports ``interval``, ``json``, ``jsonb``, ``uuid``, ``cidr``, ``inet`` and ``macaddr`` column types
(PostgreSQL 9.3 and above).
Valid Column Options
~~~~~~~~~~~~~~~~~~~~
The following are valid column options:
For any column type:
======= ===========
Option Description
======= ===========
limit set maximum length for strings, also hints column types in adapters (see note below)
length alias for ``limit``
default set default value or action
null allow ``NULL`` values, defaults to ``true`` (setting ``identity`` will override default to ``false``)
after specify the column that a new column should be placed after, or use ``\Phinx\Db\Adapter\MysqlAdapter::FIRST`` to place the column at the start of the table *(only applies to MySQL)*
comment set a text comment on the column
======= ===========
For ``decimal`` columns:
========= ===========
Option Description
========= ===========
precision combine with ``scale`` set to set decimal accuracy
scale combine with ``precision`` to set decimal accuracy
signed enable or disable the ``unsigned`` option *(only applies to MySQL)*
========= ===========
For ``enum`` and ``set`` columns:
========= ===========
Option Description
========= ===========
values Can be a comma separated list or an array of values
========= ===========
For ``smallinteger``, ``integer`` and ``biginteger`` columns:
======== ===========
Option Description
======== ===========
identity enable or disable automatic incrementing (if enabled, will set ``null: false`` if ``null`` option is not set)
signed enable or disable the ``unsigned`` option *(only applies to MySQL)*
======== ===========
For Postgres, when using ``identity``, it will utilize the ``serial`` type appropriate for the integer size, so that
``smallinteger`` will give you ``smallserial``, ``integer`` gives ``serial``, and ``biginteger`` gives ``bigserial``.
For ``timestamp`` columns:
======== ===========
Option Description
======== ===========
default set default value (use with ``CURRENT_TIMESTAMP``)
update set an action to be triggered when the row is updated (use with ``CURRENT_TIMESTAMP``) *(only applies to MySQL)*
timezone enable or disable the ``with time zone`` option for ``time`` and ``timestamp`` columns *(only applies to Postgres)*
======== ===========
You can add ``created_at`` and ``updated_at`` timestamps to a table using the ``addTimestamps()`` method. This method accepts
three arguments, where the first two allow setting alternative names for the columns while the third argument allows you to
enable the ``timezone`` option for the columns. The defaults for these arguments are ``created_at``, ``updated_at``, and ``false``
respectively. For the first and second argument, if you provide ``null``, then the default name will be used, and if you provide
``false``, then that column will not be created. Please note that attempting to set both to ``false`` will throw a
``\RuntimeException``. Additionally, you can use the ``addTimestampsWithTimezone()`` method, which is an alias to
``addTimestamps()`` that will always set the third argument to ``true`` (see examples below). The ``created_at`` column will
have a default set to ``CURRENT_TIMESTAMP``. For MySQL only, ``update_at`` column will have update set to
``CURRENT_TIMESTAMP``.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
class MyNewMigration extends AbstractMigration
{
/**
* Migrate Change.
*/
public function change()
{
// Use defaults (without timezones)
$table = $this->table('users')->addTimestamps()->create();
// Use defaults (with timezones)
$table = $this->table('users')->addTimestampsWithTimezone()->create();
// Override the 'created_at' column name with 'recorded_at'.
$table = $this->table('books')->addTimestamps('recorded_at')->create();
// Override the 'updated_at' column name with 'amended_at', preserving timezones.
// The two lines below do the same, the second one is simply cleaner.
$table = $this->table('books')->addTimestamps(null, 'amended_at', true)->create();
$table = $this->table('users')->addTimestampsWithTimezone(null, 'amended_at')->create();
// Only add the created_at column to the table
$table = $this->table('books')->addTimestamps(null, false);
// Only add the updated_at column to the table
$table = $this->table('users')->addTimestamps(false);
// Note, setting both false will throw a \RuntimeError
}
}
For ``boolean`` columns:
======== ===========
Option Description
======== ===========
signed enable or disable the ``unsigned`` option *(only applies to MySQL)*
======== ===========
For ``string`` and ``text`` columns:
========= ===========
Option Description
========= ===========
collation set collation that differs from table defaults *(only applies to MySQL)*
encoding set character set that differs from table defaults *(only applies to MySQL)*
========= ===========
For foreign key definitions:
========== ===========
Option Description
========== ===========
update set an action to be triggered when the row is updated
delete set an action to be triggered when the row is deleted
constraint set a name to be used by foreign key constraint
========== ===========
You can pass one or more of these options to any column with the optional
third argument array.
Limit Option and MySQL
~~~~~~~~~~~~~~~~~~~~~~
When using the MySQL adapter, there are a couple things to consider when working with limits:
- When using a ``string`` primary key or index on MySQL 5.7 or below, or the MyISAM storage engine, and the default charset of ``utf8mb4_unicode_ci``, you must specify a limit less than or equal to 191, or use a different charset.
- Additional hinting of database column type can be made for ``integer``, ``text``, ``blob``, ``tinyblob``, ``mediumblob``, ``longblob`` columns. Using ``limit`` with one the following options will modify the column type accordingly:
============ ==============
Limit Column Type
============ ==============
BLOB_TINY TINYBLOB
BLOB_REGULAR BLOB
BLOB_MEDIUM MEDIUMBLOB
BLOB_LONG LONGBLOB
TEXT_TINY TINYTEXT
TEXT_REGULAR TEXT
TEXT_MEDIUM MEDIUMTEXT
TEXT_LONG LONGTEXT
INT_TINY TINYINT
INT_SMALL SMALLINT
INT_MEDIUM MEDIUMINT
INT_REGULAR INT
INT_BIG BIGINT
============ ==============
For ``binary`` or ``varbinary`` types, if limit is set greater than allowed 255 bytes, the type will be changed to the best matching blob type given the length.
.. code-block:: php
<?php
use Phinx\Db\Adapter\MysqlAdapter;
//...
$table = $this->table('cart_items');
$table->addColumn('user_id', 'integer')
->addColumn('product_id', 'integer', ['limit' => MysqlAdapter::INT_BIG])
->addColumn('subtype_id', 'integer', ['limit' => MysqlAdapter::INT_SMALL])
->addColumn('quantity', 'integer', ['limit' => MysqlAdapter::INT_TINY])
->create();
Custom Column Types & Default Values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some DBMS systems provide additional column types and default values that are specific to them.
If you don't want to keep your migrations DBMS-agnostic you can use those custom types in your migrations
through the ``\Phinx\Util\Literal::from`` method, which takes a string as its only argument, and returns an
instance of ``\Phinx\Util\Literal``. When Phinx encounters this value as a column's type it knows not to
run any validation on it and to use it exactly as supplied without escaping. This also works for ``default``
values.
You can see an example below showing how to add a ``citext`` column as well as a column whose default value
is a function, in PostgreSQL. This method of preventing the built-in escaping is supported in all adapters.
.. code-block:: php
<?php
use Phinx\Migration\AbstractMigration;
use Phinx\Util\Literal;
class AddSomeColumns extends AbstractMigration
{
public function change()
{
$this->table('users')
->addColumn('username', Literal::from('citext'))
->addColumn('uniqid', 'uuid', [
'default' => Literal::from('uuid_generate_v4()')
])
->addColumn('creation', 'timestamp', [
'timezone' => true,
'default' => Literal::from('now()')
])
->create();
}
}
User Defined Types (Custom Data Domain)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Building upon the base types and column options you can define your custom
user defined types. Custom user defined types are configured in the
``data_domain`` root config option.
.. code-block:: yaml
data_domain:
phone_number:
type: string
length: 20
address_line:
type: string
length: 150
Each user defined type can hold any valid type and column option, they are just
used as "macros" and replaced at the time of migration.
.. code-block:: php