Writing Migrations

    Let’s start by creating a new Phinx migration. Run Phinx using the command:

    This will create a new migration in the formatYYYYMMDDHHMMSS_my_new_migration.php, where the first 14 characters arereplaced with the current timestamp down to the second.

    If you have specified multiple migration paths, you will be asked to selectwhich path to create the new migration in.

    Phinx automatically creates a skeleton migration file with a single method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Change Method.
    9. *
    10. * Write your reversible migrations using this method.
    11. *
    12. * More information on writing migrations is available here:
    13. * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
    14. *
    15. * The following commands can be used in this method and Phinx will
    16. * automatically reverse them when rolling back:
    17. *
    18. * createTable
    19. * renameTable
    20. * addColumn
    21. * renameColumn
    22. * addIndex
    23. * addForeignKey
    24. *
    25. * Remember to call "create()" or "update()" and NOT "save()" when working
    26. * with the Table class.
    27. */
    28. public function change()
    29. {
    30.  
    31. }
    32. }

    All Phinx migrations extend from the AbstractMigration class. This classprovides the necessary support to create your database migrations. Databasemigrations can transform your database in many ways, such as creating newtables, inserting rows, adding indexes and modifying columns.

    The Change Method

    Phinx 0.2.0 introduced a new feature called reversible migrations. This featurehas now become the default migration method. With reversible migrations, youonly need to define the change logic, and Phinx can figure out how to migratedown automatically for you. For example:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class CreateUserLoginsTable extends AbstractMigration
    6. {
    7. /**
    8. * Change Method.
    9. *
    10. * More information on this method is available here:
    11. * http://docs.phinx.org/en/latest/migrations.html#the-change-method
    12. *
    13. * Uncomment this method if you would like to use it.
    14. */
    15. public function change()
    16. {
    17. // create the table
    18. $table = $this->table('user_logins');
    19. $table->addColumn('user_id', 'integer')
    20. ->addColumn('created', 'datetime')
    21. ->create();
    22. }
    23.  
    24. /**
    25. * Migrate Up.
    26. */
    27. public function up()
    28. {
    29.  
    30. }
    31.  
    32. /**
    33. * Migrate Down.
    34. */
    35. public function down()
    36. {
    37.  
    38. }
    39. }

    When executing this migration, Phinx will create the user_logins table onthe 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 automaticallyignore the up and down methods. If you need to use these methods it isrecommended to create a separate migration file.

    Note

    When creating or updating tables inside a change() method you must usethe Table create() and update() methods. Phinx cannot automaticallydetermine whether a save() call is creating a new table or modifying anexisting one.

    Phinx can only reverse the following commands:

    • createTable
    • renameTable
    • addColumn
    • renameColumn
    • addIndex
    • addForeignKey
      If a command cannot be reversed then Phinx will throw aIrreversibleMigrationException exception when it’s migrating down.

    The Up Method

    The up method is automatically run by Phinx when you are migrating up and itdetects the given migration hasn’t been executed previously. You should use theup 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 andit detects the given migration has been executed in the past. You should usethe down method to reverse/undo the transformations described in the up method.

    Executing Queries

    Queries can be executed with the execute() and query() methods. Theexecute() method returns the number of affected rows whereas thequery() method returns the result as aPDOStatement:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. // execute()
    13. $count = $this->execute('DELETE FROM users'); // returns the number of affected rows
    14.  
    15. // query()
    16. $stmt = $this->query('SELECT * FROM users'); // returns PDOStatement
    17. $rows = $stmt->fetchAll(); // returns the result as an array
    18. }
    19.  
    20. /**
    21. * Migrate Down.
    22. */
    23. public function down()
    24. {
    25.  
    26. }
    27. }

    Note

    These commands run using the PHP Data Objects (PDO) extension whichdefines a lightweight, consistent interface for accessing databasesin PHP. Always make sure your queries abide with PDOs before usingthe execute() command. This is especially important when usingDELIMITERs during insertion of stored procedures or triggers whichdon’t support DELIMITERs.

    Warning

    When using execute() or query() with a batch of queries, PDO doesn’tthrow an exception if there is an issue with one or more of the queriesin 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 onehad an error, then Phinx would be denying access to all the results as thereis no facility in PDO to get a previous result set -but no previousSet()).

    So, as a consequence, due to the design decision in PDO to not throwan exception for batched queries, Phinx is unable to provide the fullestsupport for error handling when batches of queries are supplied.

    Fortunately though, all the features of PDO are available, so multiple batchescan be controlled within the migration by calling uponnextRowset()and examining .

    There are two methods available to fetch rows. The fetchRow() method willfetch a single row, whilst the fetchAll() method will return multiple rows.Both methods accept raw SQL as their only parameter:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. // fetch a user
    13. $row = $this->fetchRow('SELECT * FROM users');
    14.  
    15. // fetch an array of messages
    16. $rows = $this->fetchAll('SELECT * FROM messages');
    17. }
    18.  
    19. /**
    20. * Migrate Down.
    21. */
    22. public function down()
    23. {
    24.  
    25. }
    26. }

    Inserting Data

    Phinx makes it easy to insert data into your tables. Whilst this feature isintended for the , you are also free to use theinsert methods in your migrations:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class NewStatus extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. // inserting only one row
    13. $singleRow = [
    14. 'id' => 1,
    15. 'name' => 'In Progress'
    16. ];
    17.  
    18. $table = $this->table('status');
    19. $table->insert($singleRow);
    20. $table->saveData();
    21.  
    22. // inserting multiple rows
    23. $rows = [
    24. [
    25. 'id' => 2,
    26. 'name' => 'Stopped'
    27. ],
    28. [
    29. 'id' => 3,
    30. 'name' => 'Queued'
    31. ]
    32. ];
    33.  
    34. // this is a handy shortcut
    35. $this->insert('status', $rows);
    36. }
    37.  
    38. /**
    39. * Migrate Down.
    40. */
    41. public function down()
    42. {
    43. $this->execute('DELETE FROM status');
    44. }
    45. }

    Note

    The Table Object

    The Table object is one of the most useful APIs provided by Phinx. It allowsyou to easily manipulate database tables using PHP code. You can retrieve aninstance of the Table object by calling the table() method from withinyour database migration:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('tableName');
    13. }
    14.  
    15. /**
    16. * Migrate Down.
    17. */
    18. public function down()
    19. {
    20.  
    21. }
    22. }

    You can then manipulate this table using the methods provided by the Tableobject.

    The Save Method

    When working with the Table object, Phinx stores certain operations in apending changes cache.

    When in doubt, it is recommended you call this method. It will commit anypending changes to the database.

    Creating a Table

    Creating a table is really easy using the Table object. Let’s create a table tostore a collection of users:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $users = $this->table('users');
    13. $users->addColumn('username', 'string', ['limit' => 20])
    14. ->addColumn('password', 'string', ['limit' => 40])
    15. ->addColumn('password_salt', 'string', ['limit' => 40])
    16. ->addColumn('email', 'string', ['limit' => 100])
    17. ->addColumn('first_name', 'string', ['limit' => 30])
    18. ->addColumn('last_name', 'string', ['limit' => 30])
    19. ->addColumn('created', 'datetime')
    20. ->addColumn('updated', 'datetime', ['null' => true])
    21. ->addIndex(['username', 'email'], ['unique' => true])
    22. ->save();
    23. }
    24.  
    25. /**
    26. * Migrate Down.
    27. */
    28. public function down()
    29. {
    30.  
    31. }
    32. }

    Columns are added using the addColumn() method. We create a unique indexfor both the username and email columns using the addIndex() method.Finally calling save() commits the changes to the database.

    Note

    Phinx automatically creates an auto-incrementing primary key column called id for everytable.

    The id option sets the name of the automatically created identity field, while the primary_keyoption selects the field or fields used for primary key. id will always override the primary_keyoption unless it’s set to false. If you don’t need a primary key set id to false withoutspecifying a primary_key, and no primary key will be created.

    To specify an alternate primary key, you can specify the primary_key optionwhen accessing the Table object. Let’s disable the automatic id column andcreate a primary key using two columns instead:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. public function up()
    10. {
    11. $table = $this->table('followers', ['id' => false, 'primary_key' => ['user_id', 'follower_id']]);
    12. $table->addColumn('user_id', 'integer')
    13. ->addColumn('follower_id', 'integer')
    14. ->addColumn('created', 'datetime')
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }

    Setting a single primary_key doesn’t enable the option.To simply change the name of the primary key, we need to override the defaultid field name:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('followers', ['id' => 'user_id']);
    13. $table->addColumn('follower_id', 'integer')
    14. ->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }

    In addition, the MySQL adapter supports following options:

    By default the primary key is signed.To simply set it to unsigned just pass signed option with a falsevalue:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('followers', ['signed' => false]);
    13. $table->addColumn('follower_id', 'integer')
    14. ->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }

    Column types are specified as strings and can be one of:

    • biginteger
    • binary
    • boolean
    • date
    • datetime
    • decimal
    • float
    • integer
    • string
    • text
    • time
    • timestamp
    • uuid
      In addition, the MySQL adapter supports enum, set, blob and jsoncolumn types. (json in MySQL 5.7 and above)

    In addition, the Postgres adapter supports smallint, json, jsonb,uuid, cidr, inet and macaddr column types (PostgreSQL 9.3 andabove).

    For valid options, see the ref: below.

    Determining Whether a Table Exists

    You can determine whether or not a table exists by using the hasTable()method:

    Dropping a Table

    Tables can be dropped quite easily using the dropTable() method. It is agood idea to recreate the table again in the down() method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $this->dropTable('users');
    13. }
    14.  
    15. /**
    16. * Migrate Down.
    17. */
    18. public function down()
    19. {
    20. $users = $this->table('users');
    21. $users->addColumn('username', 'string', ['limit' => 20])
    22. ->addColumn('password', 'string', ['limit' => 40])
    23. ->addColumn('password_salt', 'string', ['limit' => 40])
    24. ->addColumn('email', 'string', ['limit' => 100])
    25. ->addColumn('first_name', 'string', ['limit' => 30])
    26. ->addColumn('last_name', 'string', ['limit' => 30])
    27. ->addColumn('created', 'datetime')
    28. ->addColumn('updated', 'datetime', ['null' => true])
    29. ->addIndex(['username', 'email'], ['unique' => true])
    30. ->save();
    31. }
    32. }

    Renaming a Table

    To rename a table access an instance of the Table object then call therename() method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->rename('legacy_users')
    14. ->save();
    15. }
    16.  
    17. /**
    18. * Migrate Down.
    19. */
    20. public function down()
    21. {
    22. $table = $this->table('legacy_users');
    23. $table->rename('users')
    24. ->save();
    25. }
    26. }

    Working With Columns

    Valid Column Types

    Column types are specified as strings and can be one of:

    • biginteger
    • binary
    • boolean
    • char
    • date
    • datetime
    • decimal
    • float
    • integer
    • string
    • text
    • time
    • timestamp
    • uuid
      In addition, the MySQL adapter supports enum, set, blob and json column types. (json in MySQL 5.7 and above)

    In addition, the Postgres adapter supports smallint, 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:

    OptionDescription
    limitset maximum length for strings, also hints column types in adapters (see note below)
    lengthalias for limit
    defaultset default value or action
    nullallow NULL values (should not be used with primary keys!)
    afterspecify the column that a new column should be placed after
    commentset a text comment on the column

    For decimal columns:

    OptionDescription
    precisioncombine with scale set to set integer decimal accuracy
    scalecombine with to set fractional decimal accuracy
    signedenable or disable the unsigned option (only applies to MySQL)

    For integer and biginteger columns:

    OptionDescription
    identityenable or disable automatic incrementing
    signedenable or disable the unsigned option (only applies to MySQL)

    For timestamp columns:

    OptionDescription
    defaultset default value (use with CURRENTTIMESTAMP)
    updateset an action to be triggered when the row is updated (use with CURRENT_TIMESTAMP)
    timezoneenable 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 theaddTimestamps() method. This method also allows you to supply alternativenames:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Change.
    9. */
    10. public function change()
    11. {
    12. // Override the 'updated_at' column name with 'amended_at'.
    13. $table = $this->table('users')->addTimestamps(null, 'amended_at')->create();
    14. }
    15. }

    For boolean columns:

    For string and text columns:

    OptionDescription
    collationset collation that differs from table defaults (only applies to MySQL)
    encodingset character set that differs from table defaults (only applies to MySQL)

    For foreign key definitions:

    OptionDescription
    updateset an action to be triggered when the row is updated
    deleteset an action to be triggered when the row is deleted

    You can pass one or more of these options to any column with the optionalthird argument array.

    Limit Option and PostgreSQL

    When using the PostgreSQL adapter, additional hinting of database column type can bemade for integer columns. Using limit with one the following options willmodify the column type accordingly:

    1. use Phinx\Db\Adapter\PostgresAdapter;
    2.  
    3. //...
    4.  
    5. $table = $this->table('cart_items');
    6. $table->addColumn('user_id', 'integer')
    7. ->addColumn('subtype_id', 'integer', ['limit' => PostgresAdapter::INT_SMALL])
    8. ->create();

    When using the MySQL adapter, additional hinting of database column type can bemade for integer, text and blob columns. Using limit withone the following options will modify the column type accordingly:

    LimitColumn Type
    BLOB_TINYTINYBLOB
    BLOB_REGULARBLOB
    BLOB_MEDIUMMEDIUMBLOB
    BLOB_LONGLONGBLOB
    TEXT_TINYTINYTEXT
    TEXT_REGULARTEXT
    TEXT_MEDIUMMEDIUMTEXT
    TEXT_LONGLONGTEXT
    INT_TINYTINYINT
    INT_SMALLSMALLINT
    INT_MEDIUMMEDIUMINT
    INT_REGULARINT
    INT_BIGBIGINT
    1. use Phinx\Db\Adapter\MysqlAdapter;
    2.  
    3. //...
    4.  
    5. $table = $this->table('cart_items');
    6. $table->addColumn('user_id', 'integer')
    7. ->addColumn('product_id', 'integer', ['limit' => MysqlAdapter::INT_BIG])
    8. ->addColumn('subtype_id', 'integer', ['limit' => MysqlAdapter::INT_SMALL])
    9. ->addColumn('quantity', 'integer', ['limit' => MysqlAdapter::INT_TINY])
    10. ->create();

    Get a column list

    To retrieve all table columns, simply create a table object and call _getColumns()_method. This method will return an array of Column classes with basic info. Example below:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class ColumnListMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $columns = $this->table('users')->getColumns();
    13. ...
    14. }
    15.  
    16. /**
    17. * Migrate Down.
    18. */
    19. public function down()
    20. {
    21. ...
    22. }
    23. }

    Checking whether a column exists

    You can check if a table already has a certain column by using thehasColumn() method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Change Method.
    9. */
    10. public function change()
    11. {
    12. $table = $this->table('user');
    13. $column = $table->hasColumn('username');
    14.  
    15. if ($column) {
    16. // do something
    17. }
    18.  
    19. }
    20. }

    Renaming a Column

    To rename a column, access an instance of the Table object then call therenameColumn() method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->renameColumn('bio', 'biography')
    14. ->update();
    15. }
    16.  
    17. /**
    18. * Migrate Down.
    19. */
    20. public function down()
    21. {
    22. $table = $this->table('users');
    23. $table->renameColumn('biography', 'bio')
    24. ->update();
    25. }
    26. }

    Adding a Column After Another Column

    When adding a column you can dictate its position using the after option:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Change Method.
    9. */
    10. public function change()
    11. {
    12. $table = $this->table('users');
    13. $table->addColumn('city', 'string', ['after' => 'email'])
    14. }
    15. }

    Dropping a Column

    To drop a column, use the removeColumn() method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->removeColumn('short_name')
    14. ->save();
    15. }
    16. }

    Specifying a Column Limit

    You can limit the maximum length of a column by using the limit option:

    To change column type or options on an existing column, use thechangeColumn() method. See and Valid ColumnOptions for allowed values:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $users = $this->table('users');
    13. $users->changeColumn('email', 'string', ['limit' => 255])
    14. ->save();
    15. }
    16.  
    17. /**
    18. * Migrate Down.
    19. */
    20. public function down()
    21. {
    22.  
    23. }
    24. }

    To add an index to a table you can simply call the addIndex() method on thetable object:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->addColumn('city', 'string')
    14. ->addIndex(['city'])
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }

    By default Phinx instructs the database adapter to create a normal index. Wecan pass an additional parameter unique to the addIndex() method tospecify a unique index. We can also explicitly specify a name for the indexusing the name parameter:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->addColumn('email', 'string')
    14. ->addIndex(['email'], ['unique' => true, 'name' => 'idx_users_email'])
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }

    The MySQL adapter also supports fulltext indexes. If you are using a versionbefore 5.6 you must ensure the table uses the MyISAM engine:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. public function change()
    8. {
    9. $table = $this->table('users', ['engine' => 'MyISAM']);
    10. $table->addColumn('email', 'string')
    11. ->addIndex('email', ['type' => 'fulltext'])
    12. ->create();
    13. }
    14. }

    Removing indexes is as easy as calling the removeIndex() method. You mustcall this method for each index:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('users');
    13. $table->removeIndex(['email'])
    14. ->save();
    15.  
    16. // alternatively, you can delete an index by its name, ie:
    17. $table->removeIndexByName('idx_users_email')
    18. ->save();
    19. }
    20.  
    21. /**
    22. * Migrate Down.
    23. */
    24. public function down()
    25. {
    26.  
    27. }
    28. }

    Working With Foreign Keys

    Phinx has support for creating foreign key constraints on your database tables.Let’s add a foreign key to an example table:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('tags');
    13. $table->addColumn('tag_name', 'string')
    14. ->save();
    15.  
    16. $refTable = $this->table('tag_relationships');
    17. $refTable->addColumn('tag_id', 'integer')
    18. ->addForeignKey('tag_id', 'tags', 'id', ['delete'=> 'SET_NULL', 'update'=> 'NO_ACTION'])
    19. ->save();
    20.  
    21. }
    22.  
    23. /**
    24. * Migrate Down.
    25. */
    26. public function down()
    27. {
    28.  
    29. }
    30. }

    “On delete” and “On update” actions are defined with a ‘delete’ and ‘update’options array. Possibles values are ‘SET_NULL’, ‘NO_ACTION’, ‘CASCADE’ and‘RESTRICT’. Constraint name can be changed with the ‘constraint’ option.

    It is also possible to pass addForeignKey() an array of columns. Thisallows us to establish a foreign key relationship to a table which usesa combined key:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('follower_events');
    13. $table->addColumn('user_id', 'integer')
    14. ->addColumn('follower_id', 'integer')
    15. ->addColumn('event_id', 'integer')
    16. ->addForeignKey(['user_id', 'follower_id'],
    17. 'followers',
    18. ['user_id', 'follower_id'],
    19. ['delete'=> 'NO_ACTION', 'update'=> 'NO_ACTION', 'constraint' => 'user_follower_id'])
    20. ->save();
    21. }
    22.  
    23. /**
    24. * Migrate Down.
    25. */
    26. public function down()
    27. {
    28.  
    29. }
    30. }

    We can add named foreign keys using the parameter. This featureis supported as of Phinx version 0.6.5:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('your_table');
    13. $table->addForeignKey('foreign_id', 'reference_table', ['id'],
    14. ['constraint'=>'your_foreign_key_name']);
    15. ->save();
    16. }
    17.  
    18. /**
    19. * Migrate Down.
    20. */
    21. public function down()
    22. {
    23.  
    24. }
    25. }
    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('tag_relationships');
    13. $exists = $table->hasForeignKey('tag_id');
    14. if ($exists) {
    15. // do something
    16. }
    17. }
    18.  
    19. /**
    20. * Migrate Down.
    21. */
    22. public function down()
    23. {
    24.  
    25. }
    26. }

    Finally, to delete a foreign key, use the dropForeignKey method:

    1. <?php
    2.  
    3. use Phinx\Migration\AbstractMigration;
    4.  
    5. class MyNewMigration extends AbstractMigration
    6. {
    7. /**
    8. * Migrate Up.
    9. */
    10. public function up()
    11. {
    12. $table = $this->table('tag_relationships');
    13. $table->dropForeignKey('tag_id');
    14. }
    15.  
    16. /**
    17. * Migrate Down.
    18. */
    19. public function down()
    20. {
    21.  
    22. }