Migrations are a declarative API that helps you 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 Migrations API, but raw SQL is also supported.
Adding Columns
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
binaryuuid
nativeuuid
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).
With most adapters, the uuid
and nativeuuid
column types are aliases,
however with the MySQL adapter + MariaDB, the nativeuuid
type maps to
a native uuid column instead of CHAR(36)
like uuid
does.
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 \Migrations\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
and float
columns:
Option |
Description |
precision |
total number of digits (e.g., 10 in DECIMAL(10,2) ) |
scale |
number of digits after the decimal point (e.g., 2 in DECIMAL(10,2) ) |
signed |
enable or disable the unsigned option (only applies to MySQL) |
Note
Precision and Scale Terminology
Migrations follows the SQL standard where precision
represents the total number of digits,
and scale
represents digits after the decimal point. For example, to create DECIMAL(10,2)
(10 total digits with 2 decimal places):
$table->addColumn('price', 'decimal', [
'precision' => 10, // Total digits
'scale' => 2, // Decimal places
]);
This differs from CakePHP’s TableSchema which uses length
for total digits and
precision
for decimal places. The migration adapter handles this conversion automatically.
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:
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
and updated
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
, updated
, 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
column will have a default set to CURRENT_TIMESTAMP
. For MySQL
only, updated
column will have update set to
CURRENT_TIMESTAMP
:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Change.
*/
public function change(): void
{
// Use defaults (without timezones)
$table = $this->table('users')->addTimestamps()->create();
// Use defaults (with timezones)
$table = $this->table('users')->addTimestampsWithTimezone()->create();
// Override the 'created' column name with 'recorded_at'.
$table = $this->table('books')->addTimestamps('recorded_at')->create();
// Override the 'updated' 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 column to the table
$table = $this->table('books')->addTimestamps(null, false);
// Only add the updated 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) |
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:
<?php
use Migrations\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();
Default values with expressions
If you need to set a default to an expression, you can use a Literal
to have
the column’s default value used without any quoting or escaping. This is helpful
when you want to use a function as a default value:
use Migrations\BaseMigration;
use Migrations\Db\Literal;
class AddSomeColumns extends BaseMigration
{
public function change(): void
{
$this->table('users')
->addColumn('uniqid', 'uuid', [
'default' => Literal::from('uuid_generate_v4()')
])
->create();
}
}
Creating a Table
Creating a table is really easy using the Table object. Let’s create a table to
store a collection of users:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$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
Migrations 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:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$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:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function up(): void
{
$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 |
Platform |
Description |
comment |
MySQL, Postgres |
set a text comment on the table |
collation |
MySQL, SqlServer |
the default collation for a table if different than the database. |
row_format |
MySQL |
set the table row format |
engine |
MySQL |
define table engine (defaults to ``InnoDB``) |
collation |
MySQL |
define table collation (defaults to ``utf8mb4_unicode_ci``) |
signed |
MySQL |
whether the primary key is signed (defaults to ``false``) |
limit |
MySQL |
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:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('followers', ['signed' => false]);
$table->addColumn('follower_id', 'integer')
->addColumn('created', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
->create();
}
}
If you need to create a table with a different collation than the database,
use:
<?php
use Migrations\BaseMigration;
class CreateCategoriesTable extends BaseMigration
{
public function change(): void
{
$table = $this
->table('categories', [
'collation' => 'latin1_german1_ci'
])
->addColumn('title', 'string')
->create();
}
}
Note however this can only be done on table creation : there is currently no way
of adding a column to an existing table with a different collation than the
table or the database. Only MySQL
and SqlServer
supports this
configuration key for the time being.
To view available column types and options, see Adding Columns for details.
Saving Changes
When working with the Table object, Migrations 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, Migrations 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.
Renaming a Column
To rename a column, access an instance of the Table object then call the
renameColumn()
method:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table->renameColumn('bio', 'biography')
->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
$table = $this->table('users');
$table->renameColumn('biography', 'bio')
->save();
}
}
Adding a Column After Another Column
When adding a column with the MySQL adapter, you can dictate its position using
the after
option, where its value is the name of the column to position it
after:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Change Method.
*/
public function change(): void
{
$table = $this->table('users');
$table->addColumn('city', 'string', ['after' => 'email'])
->update();
}
}
This would create the new column city
and position it after the email
column. The \Migrations\Db\Adapter\MysqlAdapter::FIRST
constant can be used
to specify that the new column should be created as the first column in that
table.
Dropping a Column
To drop a column, use the removeColumn()
method:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate up.
*/
public function up(): void
{
$table = $this->table('users');
$table->removeColumn('short_name')
->save();
}
}
Specifying a Column Limit
You can limit the maximum length of a column by using the limit
option:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Change Method.
*/
public function change(): void
{
$table = $this->table('tags');
$table->addColumn('short_name', 'string', ['limit' => 30])
->update();
}
}
Changing Column Attributes
To change column type or options on an existing column, use the changeColumn()
method.
See valid-column-types and Valid Column Options for allowed values:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$users = $this->table('users');
$users->changeColumn('email', 'string', ['limit' => 255])
->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
Working With Indexes
To add an index to a table you can simply call the addIndex()
method on the
table object:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table->addColumn('city', 'string')
->addIndex(['city'])
->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
By default Migrations instructs the database adapter to create a simple index. We
can pass an additional parameter unique
to the addIndex()
method to
specify a unique index. We can also explicitly specify a name for the index
using the name
parameter, the index columns sort order can also be specified using
the order
parameter. The order parameter takes an array of column names and sort order key/value pairs:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addColumn('username','string')
->addIndex(['email', 'username'], [
'unique' => true,
'name' => 'idx_users_email',
'order' => ['email' => 'DESC', 'username' => 'ASC']]
)
->save();
}
}
As of 4.6.0, you can use BaseMigration::index()
to get a fluent builder to
define indexes:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addColumn('username','string')
->addIndex(
$this->index(['email', 'username'])
->setType('unique')
->setName('idx_users_email')
->setOrder(['email' => 'DESC', 'username' => 'ASC'])
)
->save();
}
}
The MySQL adapter also supports fulltext
indexes. If you are using a version before 5.6 you must
ensure the table uses the MyISAM
engine:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users', ['engine' => 'MyISAM']);
$table->addColumn('email', 'string')
->addIndex('email', ['type' => 'fulltext'])
->create();
}
}
MySQL adapter supports setting the index length defined by limit option.
When you are using a multi-column index, you are able to define each column index length.
The single column index can define its index length with or without defining column name in limit option:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addColumn('username','string')
->addColumn('user_guid', 'string', ['limit' => 36])
->addIndex(['email','username'], ['limit' => ['email' => 5, 'username' => 2]])
->addIndex('user_guid', ['limit' => 6])
->create();
}
}
The SQL Server and PostgreSQL adapters support include
(non-key) columns on indexes:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addColumn('firstname','string')
->addColumn('lastname','string')
->addIndex(['email'], ['include' => ['firstname', 'lastname']])
->create();
}
}
PostgreSQL, SQLServer, and SQLite support partial indexes by defining where
clauses for the index:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addColumn('is_verified','boolean')
->addIndex(
$this->index('email')
->setName('user_email_verified_idx')
->setType('unique')
->setWhere('is_verified = true')
)
->create();
}
}
PostgreSQL can create indexes concurrently which avoids taking disruptive locks
during index creation:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users');
$table->addColumn('email', 'string')
->addIndex(
$this->index('email')
->setName('user_email_unique_idx')
->setType('unique')
->setConcurrently(true)
)
->create();
}
}
PostgreSQL adapters also supports Generalized Inverted Index gin
indexes:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function change(): void
{
$table = $this->table('users');
$table->addColumn('address', 'string')
->addIndex('address', ['type' => 'gin'])
->create();
}
}
Removing indexes is as easy as calling the removeIndex()
method. You must
call this method for each index:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table->removeIndex(['email'])
->save();
// alternatively, you can delete an index by its name, ie:
$table->removeIndexByName('idx_users_email')
->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
New in version 4.6.0: Index::setWhere()
, and Index::setConcurrently()
were added.
Working With Foreign Keys
Migrations has support for creating foreign key constraints on your database tables.
Let’s add a foreign key to an example table:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('tags');
$table->addColumn('tag_name', 'string')
->save();
$refTable = $this->table('tag_relationships');
$refTable->addColumn('tag_id', 'integer', ['null' => true])
->addForeignKey(
'tag_id',
'tags',
'id',
['delete'=> 'SET_NULL', 'update'=> 'NO_ACTION'],
)
->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
The ‘delete’ and ‘update’ options allow you to define the ON UPDATE
and ON
DELETE
behavior. Possibles values are ‘SET_NULL’, ‘NO_ACTION’, ‘CASCADE’ and
‘RESTRICT’. If ‘SET_NULL’ is used then the column must be created as nullable
with the option ['null' => true]
.
Foreign keys can be defined with arrays of columns to build constraints between
tables with composite keys:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
public function up(): void
{
$table = $this->table('follower_events');
$table->addColumn('user_id', 'integer')
->addColumn('follower_id', 'integer')
->addColumn('event_id', 'integer')
->addForeignKey(
['user_id', 'follower_id'],
'followers',
['user_id', 'follower_id'],
[
'delete'=> 'NO_ACTION',
'update'=> 'NO_ACTION',
'constraint' => 'user_follower_id',
]
)
->save();
}
}
The options parameter of addForeignKey()
supports the following options:
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 |
deferrable |
define deferred constraint application (postgres only) |
Using the foreignKey()
method provides a fluent builder to define a foreign
key:
<?php
use Migrations\BaseMigration;
use Migrations\Db\Table\ForeignKey;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('articles');
$table->addForeignKey(
$this->foreignKey()
->setColumns('user_id')
->setReferencedTable('users')
->setReferencedColumns('user_id')
->setDelete(ForeignKey::CASCADE)
->setName('article_user_fk')
)
->save();
}
}
New in version 4.6.0: The foreignKey
method was added.
We can also easily check if a foreign key exists:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('tag_relationships');
$exists = $table->hasForeignKey('tag_id');
if ($exists) {
// do something
}
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
Finally, to delete a foreign key, use the dropForeignKey
method.
Note that like other methods in the Table
class, dropForeignKey
also
needs save()
to be called at the end in order to be executed. This allows
Migrations to intelligently plan migrations when more than one table is
involved:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('tag_relationships');
$table->dropForeignKey('tag_id')->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
Determining Whether a Table Exists
You can determine whether or not a table exists by using the hasTable()
method:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$exists = $this->hasTable('users');
if ($exists) {
// do something
}
}
/**
* Migrate Down.
*/
public function down(): void
{
}
}
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 Migrations to intelligently
plan migrations when more than one table is involved:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$this->table('users')->drop()->save();
}
/**
* Migrate Down.
*/
public function down(): void
{
$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:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$table = $this->table('users');
$table
->rename('legacy_users')
->update();
}
/**
* Migrate Down.
*/
public function down(): void
{
$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:
<?php
use Migrations\BaseMigration;
class MyNewMigration extends BaseMigration
{
/**
* Migrate Up.
*/
public function up(): void
{
$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(): void
{
}
}
Creating Custom Primary Keys
You can specify a autoId
property in the Migration class and set it to
false
, which will turn off the automatic id
column creation. You will
need to manually create the column that will be used as a primary key and add
it to the table declaration:
<?php
use Migrations\BaseMigration;
class CreateProductsTable extends BaseMigration
{
public bool $autoId = false;
public function up(): void
{
$table = $this->table('products');
$table
->addColumn('id', 'uuid')
->addPrimaryKey('id')
->addColumn('name', 'string')
->addColumn('description', 'text')
->create();
}
}
The above will create a CHAR(36)
id
column that is also the primary key.
When specifying a custom primary key on the command line, you must note
it as the primary key in the id field, otherwise you may get an error
regarding duplicate id fields, i.e.:
bin/cake bake migration CreateProducts id:uuid:primary name:string description:text created modified
All baked migrations and snapshot will use this new way when necessary.
Warning
Dealing with primary key can only be done on table creation operations.
This is due to limitations for some database servers the plugin supports.