diff --git a/docs/.vitepress/toc_en.json b/docs/.vitepress/toc_en.json
index 788a3039e..a50f01a4e 100644
--- a/docs/.vitepress/toc_en.json
+++ b/docs/.vitepress/toc_en.json
@@ -1,21 +1,43 @@
{
"/": [
{
- "text": "Migrations",
+ "text": "Getting Started",
"collapsed": false,
"items": [
- { "text": "Writing Migrations", "link": "/writing-migrations" },
- { "text": "Using the Query Builder", "link": "/using-the-query-builder" },
- { "text": "Executing Queries", "link": "/executing-queries" },
- { "text": "Database Seeding", "link": "/seeding" }
+ { "text": "Migrations 5.x", "link": "/" },
+ { "text": "Installation and Overview", "link": "/getting-started/installation-and-overview" },
+ { "text": "Creating Migrations", "link": "/getting-started/creating-migrations" },
+ { "text": "Snapshots and Diffs", "link": "/getting-started/snapshots-and-diffs" },
+ { "text": "Running and Managing Migrations", "link": "/getting-started/running-and-managing-migrations" }
+ ]
+ },
+ {
+ "text": "Guides",
+ "collapsed": false,
+ "items": [
+ { "text": "Integration and Deployment", "link": "/advanced/integration-and-deployment" },
+ {
+ "text": "Writing Migrations",
+ "link": "/guides/writing-migrations/migration-methods",
+ "items": [
+ { "text": "Migration Methods", "link": "/guides/writing-migrations/migration-methods" },
+ { "text": "Columns and Table Operations", "link": "/guides/writing-migrations/columns-and-table-operations" },
+ { "text": "Indexes and Constraints", "link": "/guides/writing-migrations/indexes-and-constraints" },
+ { "text": "Schema Introspection and Platform Limitations", "link": "/guides/writing-migrations/schema-introspection-and-platform-limitations" }
+ ]
+ },
+ { "text": "Using the Query Builder", "link": "/guides/using-the-query-builder" },
+ { "text": "Executing Queries", "link": "/guides/executing-queries" },
+ { "text": "Inserting Data", "link": "/guides/inserting-data" },
+ { "text": "Database Seeding", "link": "/guides/seeding" }
]
},
{
"text": "Upgrade Guides",
"collapsed": true,
"items": [
- { "text": "Upgrading from 4.x to 5.x", "link": "/upgrading" },
- { "text": "Upgrading to the Builtin Backend", "link": "/upgrading-to-builtin-backend" }
+ { "text": "Upgrading from 4.x to 5.x", "link": "/upgrades/upgrading-from-4-x" },
+ { "text": "Upgrading to the Builtin Backend", "link": "/upgrades/upgrading-to-builtin-backend" }
]
}
]
diff --git a/docs/en/advanced/integration-and-deployment.md b/docs/en/advanced/integration-and-deployment.md
new file mode 100644
index 000000000..35be96e63
--- /dev/null
+++ b/docs/en/advanced/integration-and-deployment.md
@@ -0,0 +1,229 @@
+# Integration and Deployment
+
+This guide covers test integration, plugin usage, programmatic execution, and
+deployment-related operational concerns.
+
+## Using Migrations for Tests
+
+If you are using migrations for your application schema, you can also use those
+same migrations to build schema in your tests. In your application's
+`tests/bootstrap.php` file you can use the `Migrator` class to build schema
+when tests are run. The `Migrator` will use existing schema if it is current,
+and if the migration history in the database differs from what is in the
+filesystem, all tables will be dropped and migrations will be rerun from the
+beginning:
+
+```php
+// in tests/bootstrap.php
+use Migrations\TestSuite\Migrator;
+
+$migrator = new Migrator();
+
+// Simple setup with no plugins
+$migrator->run();
+
+// Run a non-test database
+$migrator->run(['connection' => 'test_other']);
+
+// Run migrations for plugins
+$migrator->run(['plugin' => 'Contacts']);
+
+// Run the Documents migrations on the test_docs connection
+$migrator->run(['plugin' => 'Documents', 'connection' => 'test_docs']);
+```
+
+If you need to run multiple sets of migrations, use `runMany()`:
+
+```php
+$migrator->runMany([
+ ['plugin' => 'Contacts'],
+ ['plugin' => 'Documents', 'connection' => 'test_docs'],
+]);
+```
+
+If your database also contains tables that are not managed by your application,
+such as those created by PostGIS, you can exclude those tables from drop and
+truncate behavior using the `skip` option:
+
+```php
+$migrator->run(['connection' => 'test', 'skip' => ['postgis*']]);
+```
+
+The `skip` option accepts an `fnmatch()` compatible pattern.
+
+## Using Migrations in Plugins
+
+Plugins can also provide migration files. All commands in the Migrations plugin
+support the `--plugin` or `-p` option to scope execution to the migrations
+relative to that plugin:
+
+```bash
+bin/cake migrations status -p PluginName
+bin/cake migrations migrate -p PluginName
+```
+
+## Running Migrations in a Non-shell Environment
+
+While typical usage of migrations is from the command line, you can also run
+migrations from a non-shell environment by using the `Migrations\Migrations`
+class. This can be handy when you are developing a plugin installer for a CMS,
+for instance.
+
+The `Migrations` class allows you to run the following commands:
+
+- `migrate`
+- `rollback`
+- `markMigrated`
+- `status`
+- `seed`
+
+Each of these commands has a corresponding method defined in the
+`Migrations\Migrations` class.
+
+```php
+use Migrations\Migrations;
+
+$migrations = new Migrations();
+
+$status = $migrations->status();
+$migrate = $migrations->migrate();
+$rollback = $migrations->rollback();
+$markMigrated = $migrations->markMigrated(20150804222900);
+$seeded = $migrations->seed();
+```
+
+The methods can accept an array of parameters that match the shell command
+options:
+
+```php
+use Migrations\Migrations;
+
+$migrations = new Migrations();
+$status = $migrations->status([
+ 'connection' => 'custom',
+ 'source' => 'MyMigrationsFolder',
+]);
+```
+
+The only exception is `markMigrated()`, which expects the version number as the
+first argument and the options array as the second.
+
+Optionally, you can pass default parameters in the constructor:
+
+```php
+use Migrations\Migrations;
+
+$migrations = new Migrations([
+ 'connection' => 'custom',
+ 'source' => 'MyMigrationsFolder',
+]);
+
+$status = $migrations->status();
+$migrate = $migrations->migrate();
+```
+
+If you need to override one or more default parameters for one call, pass them
+to the method:
+
+```php
+use Migrations\Migrations;
+
+$migrations = new Migrations([
+ 'connection' => 'custom',
+ 'source' => 'MyMigrationsFolder',
+]);
+
+$status = $migrations->status();
+$migrate = $migrations->migrate(['connection' => 'default']);
+```
+
+## Feature Flags
+
+Migrations offers a few feature flags for compatibility. These features are
+disabled by default but can be enabled if required:
+
+- `unsigned_primary_keys`: Should Migrations create primary keys as unsigned
+ integers? Default: `false`
+- `unsigned_ints`: Should Migrations create all integer columns as unsigned?
+ Default: `false`
+- `column_null_default`: Should Migrations create columns as nullable by
+ default? Default: `false`
+- `add_timestamps_use_datetime`: Should Migrations use `DATETIME` type columns
+ for the columns added by `addTimestamps()`?
+
+Set them via `Configure`, for example in `config/app.php`:
+
+```text
+'Migrations' => [
+ 'unsigned_primary_keys' => true,
+ 'unsigned_ints' => true,
+ 'column_null_default' => true,
+],
+```
+
+> [!NOTE]
+> The `unsigned_primary_keys` and `unsigned_ints` options only affect MySQL
+> databases. When generating migrations with `bake migration_snapshot` or
+> `bake migration_diff`, the `signed` attribute will only be included in the
+> output for unsigned columns as `'signed' => false`.
+
+## Skipping the `schema.lock` File Generation
+
+In order for the diff feature to work, a `.lock` file is generated every time
+you migrate, roll back, or bake a snapshot, to keep track of the state of your
+database schema at any given point in time. You can skip this file generation,
+for instance when deploying to production, by using the `--no-lock` option:
+
+```bash
+bin/cake migrations migrate --no-lock
+bin/cake migrations rollback --no-lock
+bin/cake bake migration_snapshot MyMigration --no-lock
+```
+
+## Deployment
+
+You should update your deployment scripts to run migrations when new code is
+deployed. Ideally, run migrations after the code is on your servers, but before
+the application code becomes active.
+
+After running migrations, remember to clear the ORM cache so it renews the
+column metadata of your tables. Otherwise, you might end up with errors about
+columns not existing when performing operations on those new columns. The
+CakePHP core includes a [Schema Cache Shell](https://book.cakephp.org/5/en/console-and-shells/schema-cache.html) that you can use:
+
+```bash
+bin/cake migration migrate
+bin/cake schema_cache clear
+```
+
+## Alert of Missing Migrations
+
+You can use the `Migrations.PendingMigrations` middleware in local development
+to alert developers about new migrations that have not been applied:
+
+```php
+use Migrations\Middleware\PendingMigrationsMiddleware;
+
+$config = [
+ 'plugins' => [
+ // Optionally include a list of plugins with migrations to check.
+ ],
+];
+
+$middlewareQueue
+ // ErrorHandler middleware
+ ->add(new PendingMigrationsMiddleware($config));
+```
+
+You can add the `'app'` config key set to `false` if you are only interested in
+checking plugin migrations.
+
+You can temporarily disable the migration check by adding
+`skip-migration-check=1` to the URL query string.
+
+## IDE Autocomplete Support
+
+The [IdeHelper plugin](https://github.com/dereuromark/cakephp-ide-helper) can
+help you get more IDE support for tables, their column names, and possible
+column types. Specifically, PHPStorm understands the meta information and can
+help you autocomplete those.
diff --git a/docs/en/contents.md b/docs/en/contents.md
deleted file mode 100644
index 18fe41d4c..000000000
--- a/docs/en/contents.md
+++ /dev/null
@@ -1,8 +0,0 @@
-### CakePHP Migrations
-
-- [Migrations 5.x](index)
-- [Writing Migrations](writing-migrations)
-- [Using the Query Builder](using-the-query-builder)
-- [Executing Queries](executing-queries)
-- [Database Seeding](seeding)
-- [Upgrading to the builtin backend](upgrading-to-builtin-backend)
diff --git a/docs/en/getting-started/creating-migrations.md b/docs/en/getting-started/creating-migrations.md
new file mode 100644
index 000000000..f99f26087
--- /dev/null
+++ b/docs/en/getting-started/creating-migrations.md
@@ -0,0 +1,314 @@
+# Creating Migrations
+
+Migration files are stored in the `config/Migrations` directory of your
+application. The names of the migration files are prefixed with the date in
+which they were created, in the format
+`YYYYMMDDHHMMSS_MigrationName.php`.
+
+Examples:
+
+- `20160121163850_CreateProducts.php`
+- `20160210133047_AddRatingToProducts.php`
+
+The easiest way to create a migration file is by using `bin/cake bake migration`:
+
+```bash
+bin/cake bake migration CreateProducts
+```
+
+This creates an empty migration that you can edit to add any columns, indexes,
+and foreign keys you need. See [Writing Migrations](../guides/writing-migrations)
+for more information on using `Table` objects to define schema changes.
+
+> [!NOTE]
+> Migrations need to be applied using `bin/cake migrations migrate` after they
+> have been created.
+
+## Migration File Names
+
+When generating a migration, you can follow one of the following patterns to
+have additional skeleton code generated:
+
+- `/^(Create)(.*)/` Creates the specified table.
+- `/^(Drop)(.*)/` Drops the specified table and ignores specified field arguments.
+- `/^(Add).*(?:To)(.*)/` Adds fields to the specified table.
+- `/^(Remove).*(?:From)(.*)/` Removes fields from the specified table.
+- `/^(Alter)(.*)/` Alters the specified table. An alias for create-table and
+ add-field generation.
+- `/^(Alter).*(?:On)(.*)/` Alters fields from the specified table.
+
+You can also use the `underscore_form` as the name for your migrations, such as
+`create_products`.
+
+> [!WARNING]
+> Migration names are used as class names, and thus may collide with other
+> migrations if the class names are not unique. In that case, you may need to
+> rename the migration manually.
+
+## Creating a Table
+
+You can use `bake migration` to create a table:
+
+```bash
+bin/cake bake migration CreateProducts name:string description:text created modified
+```
+
+The command above will generate a migration file that resembles:
+
+```php
+table('products');
+ $table->addColumn('name', 'string', [
+ 'default' => null,
+ 'limit' => 255,
+ 'null' => false,
+ ]);
+ $table->addColumn('description', 'text', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->addColumn('created', 'datetime', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->addColumn('modified', 'datetime', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->create();
+ }
+}
+```
+
+## Column Syntax
+
+The `bake migration` command provides a compact syntax to define columns when
+generating a migration:
+
+```bash
+bin/cake bake migration CreateProducts name:string description:text created modified
+```
+
+You can use the column syntax when creating tables and adding columns. You can
+also edit the generated migration afterwards to customize the columns further.
+
+Columns on the command line follow this pattern:
+
+```text
+fieldName:fieldType?[length]:default[value]:indexType:indexName
+```
+
+Examples of valid email field definitions:
+
+- `email:string?`
+- `email:string:unique`
+- `email:string?[50]`
+- `email:string:unique:EMAIL_INDEX`
+- `email:string[120]:unique:EMAIL_INDEX`
+
+While defining decimal columns, the `length` can include precision and scale:
+
+- `amount:decimal[5,2]`
+- `amount:decimal?[5,2]`
+
+Columns with a question mark after the field type are nullable.
+
+The `length` part is optional and should always be written between brackets.
+
+The `default[value]` part is optional and sets the default value for the
+column. Supported value types include:
+
+- Booleans: `true` or `false` such as `active:boolean:default[true]`
+- Integers: `0`, `123`, `-456` such as `count:integer:default[0]`
+- Floats: `1.5`, `-2.75` such as `rate:decimal:default[1.5]`
+- Strings: `'hello'` or `"world"` such as
+ `status:string:default['pending']`
+- Null: `null` or `NULL` such as `description:text?:default[null]`
+- SQL expressions: `CURRENT_TIMESTAMP` such as
+ `created_at:datetime:default[CURRENT_TIMESTAMP]`
+
+Fields named `created` and `modified`, as well as any field with an `_at`
+suffix, will automatically be set to the type `datetime`.
+
+There are some heuristics for choosing field types when left unspecified or set
+to an invalid value. The default field type is `string`:
+
+- `id`: `integer`
+- `created`, `modified`, `updated`: `datetime`
+- `latitude`, `longitude`, `lat`, `lng`: `decimal`
+
+Additionally, you can create an empty migration file if you want full control
+over what needs to be executed by omitting column definitions:
+
+```bash
+bin/cake migrations create MyCustomMigration
+```
+
+## Adding Columns to an Existing Table
+
+If the migration name is of the form `AddXXXToYYY` and is followed by a list of
+column names and types, a migration file containing the code for creating the
+columns will be generated:
+
+```bash
+bin/cake bake migration AddPriceToProducts price:decimal[5,2]
+```
+
+This generates:
+
+```php
+table('products');
+ $table->addColumn('price', 'decimal', [
+ 'default' => null,
+ 'null' => false,
+ 'precision' => 5,
+ 'scale' => 2,
+ ]);
+ $table->update();
+ }
+}
+```
+
+## Adding a Column with an Index
+
+It is also possible to add indexes to columns:
+
+```bash
+bin/cake bake migration AddNameIndexToProducts name:string:index
+```
+
+This will generate:
+
+```php
+table('products');
+ $table->addColumn('name', 'string')
+ ->addColumn('email', 'string')
+ ->addIndex(['name'])
+ // add a unique index:
+ ->addIndex('email', ['unique' => true])
+ ->update();
+ }
+}
+```
+
+## Adding a Column with a Default Value
+
+You can specify default values for columns using the `default[value]` syntax:
+
+```bash
+bin/cake bake migration AddActiveToUsers active:boolean:default[true]
+```
+
+This will generate:
+
+```php
+table('users');
+ $table->addColumn('active', 'boolean', [
+ 'default' => true,
+ 'null' => false,
+ ]);
+ $table->update();
+ }
+}
+```
+
+You can combine default values with other options like nullable and indexes:
+
+```bash
+bin/cake bake migration AddStatusToOrders status:string:default['pending']:unique
+```
+
+## Altering a Column
+
+In the same way, you can generate a migration to alter a column if the
+migration name is of the form `AlterXXXOnYYY`:
+
+```bash
+bin/cake bake migration AlterPriceOnProducts name:float
+```
+
+This will generate:
+
+```php
+table('products');
+ $table->changeColumn('name', 'float');
+ $table->update();
+ }
+}
+```
+
+> [!WARNING]
+> Changing the type of a column can result in data loss if the current and
+> target column type are not compatible. For example, converting a varchar to a
+> float.
+
+## Removing a Column
+
+In the same way, you can generate a migration to remove a column if the
+migration name is of the form `RemoveXXXFromYYY`:
+
+```bash
+bin/cake bake migration RemovePriceFromProducts price
+```
+
+This creates:
+
+```php
+table('products');
+ $table->removeColumn('price')
+ ->save();
+ }
+}
+```
+
+> [!NOTE]
+> `removeColumn()` is not reversible, so it must be called in the `up()`
+> method. Add a corresponding `addColumn()` call to the `down()` method.
diff --git a/docs/en/getting-started/installation-and-overview.md b/docs/en/getting-started/installation-and-overview.md
new file mode 100644
index 000000000..940a197ac
--- /dev/null
+++ b/docs/en/getting-started/installation-and-overview.md
@@ -0,0 +1,101 @@
+# Installation and Overview
+
+This guide covers installation, plugin loading, and the basic migration model
+used by the Migrations plugin.
+
+## Installation
+
+By default Migrations is installed with the application skeleton. If you've
+removed it and want to re-install it, run the following from your application's
+root directory:
+
+```bash
+php composer.phar require cakephp/migrations "@stable"
+
+# Or if composer is installed globally
+composer require cakephp/migrations "@stable"
+```
+
+To use the plugin, load it in your application's `config/bootstrap.php` file:
+
+```bash
+bin/cake plugin load Migrations
+```
+
+Or load the plugin in `src/Application.php`:
+
+```php
+$this->addPlugin('Migrations');
+```
+
+Additionally, configure the default database connection in `config/app.php` as
+explained in the [Database Configuration section](https://book.cakephp.org/5/en/orm/database-basics.html#database-configuration).
+
+## Overview
+
+A migration is a PHP file that describes the changes to apply to your database.
+A migration file can add, change, or remove tables, columns, indexes, and
+foreign keys.
+
+If we wanted to create a table, we could use a migration similar to this:
+
+```php
+table('products');
+ $table->addColumn('name', 'string', [
+ 'default' => null,
+ 'limit' => 255,
+ 'null' => false,
+ ]);
+ $table->addColumn('description', 'text', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->addColumn('created', 'datetime', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->addColumn('modified', 'datetime', [
+ 'default' => null,
+ 'null' => false,
+ ]);
+ $table->create();
+ }
+}
+```
+
+When applied, this migration will add a table to your database named
+`products` with the following column definitions:
+
+- `id` column of type `integer` as primary key. This column is added
+ implicitly, but you can customize the name and type if necessary.
+- `name` column of type `string`
+- `description` column of type `text`
+- `created` column of type `datetime`
+- `modified` column of type `datetime`
+
+> [!NOTE]
+> Migrations are not automatically applied. Use the CLI commands to apply and
+> roll back migrations.
+
+Once the file has been created in the `config/Migrations` folder, you can apply
+it:
+
+```bash
+bin/cake migrations migrate
+```
+
+## Next Steps
+
+- Use [Creating Migrations](creating-migrations) to generate migration files
+ with `bake`
+- Use [Running and Managing Migrations](running-and-managing-migrations) to
+ apply, roll back, and inspect migrations
+- Use [Writing Migrations](../guides/writing-migrations) for the full Table API and
+ migration authoring reference
diff --git a/docs/en/getting-started/running-and-managing-migrations.md b/docs/en/getting-started/running-and-managing-migrations.md
new file mode 100644
index 000000000..f3c98fda8
--- /dev/null
+++ b/docs/en/getting-started/running-and-managing-migrations.md
@@ -0,0 +1,135 @@
+# Running and Managing Migrations
+
+This guide covers the commands you use after writing or generating migration
+files.
+
+## Applying Migrations
+
+Once you have generated or written your migration file, apply the changes to
+your database:
+
+```bash
+# Run all the migrations
+bin/cake migrations migrate
+
+# Migrate to a specific version using the --target option
+bin/cake migrations migrate -t 20150103081132
+
+# Run migrations from a custom source directory
+bin/cake migrations migrate -s Alternate
+
+# Run migrations against a different connection
+bin/cake migrations migrate -c my_custom_connection
+
+# Run migrations for a plugin
+bin/cake migrations migrate -p MyAwesomePlugin
+```
+
+## Reverting Migrations
+
+The `rollback` command undoes previously executed migrations:
+
+```bash
+# Roll back to the previous migration
+bin/cake migrations rollback
+
+# Roll back to a specific version
+bin/cake migrations rollback -t 20150103081132
+```
+
+You can also use the `--source`, `--connection`, and `--plugin` options just
+like for the `migrate` command.
+
+## Viewing Migration Status
+
+The `status` command prints a list of all migrations, along with their current
+status:
+
+```bash
+bin/cake migrations status
+```
+
+You can also output the results as JSON:
+
+```bash
+bin/cake migrations status --format json
+```
+
+You can also use the `--source`, `--connection`, and `--plugin` options just
+like for the `migrate` command.
+
+### Cleaning Up Missing Migrations
+
+Sometimes migration files may be deleted from the filesystem but still exist in
+the migrations tracking table. These migrations will be marked as `MISSING` in
+the status output. You can remove these entries using the `--cleanup` option:
+
+```bash
+bin/cake migrations status --cleanup
+```
+
+This will remove all migration entries from the tracking table that no longer
+have corresponding migration files in the filesystem.
+
+## Marking a Migration as Migrated
+
+It can sometimes be useful to mark a set of migrations as migrated without
+actually running them. In order to do this, use the `mark_migrated` command.
+
+You can mark all migrations as migrated:
+
+```bash
+bin/cake migrations mark_migrated
+```
+
+You can also mark all migrations up to a specific version using the `--target`
+option:
+
+```bash
+bin/cake migrations mark_migrated --target=20151016204000
+```
+
+If you do not want the targeted migration to be marked as migrated during the
+process, use the `--exclude` flag:
+
+```bash
+bin/cake migrations mark_migrated --target=20151016204000 --exclude
+```
+
+If you wish to mark only the targeted migration as migrated, use the `--only`
+flag:
+
+```bash
+bin/cake migrations mark_migrated --target=20151016204000 --only
+```
+
+You can also use the `--source`, `--connection`, and `--plugin` options just
+like for the `migrate` command.
+
+> [!NOTE]
+> When you bake a snapshot with `cake bake migration_snapshot`, the created
+> migration will automatically be marked as migrated. To prevent this behavior,
+> for example for unit test migrations, use the `--generate-only` flag.
+
+This command also accepts the migration version number as a positional
+argument:
+
+```bash
+bin/cake migrations mark_migrated 20150420082532
+```
+
+If you wish to mark all migrations as migrated, you can use the `all` special
+value:
+
+```bash
+bin/cake migrations mark_migrated all
+```
+
+## Seeding Your Database
+
+Seed classes are a good way to populate your database with default or starter
+data. They are also useful for generating data for development environments.
+
+By default, seeds are looked for in the `config/Seeds/` directory of your
+application. See [Database Seeding](../guides/seeding) for how to build and use seed
+classes.
diff --git a/docs/en/getting-started/snapshots-and-diffs.md b/docs/en/getting-started/snapshots-and-diffs.md
new file mode 100644
index 000000000..a5b8f1d9c
--- /dev/null
+++ b/docs/en/getting-started/snapshots-and-diffs.md
@@ -0,0 +1,116 @@
+# Snapshots and Diffs
+
+This guide covers bootstrapping migrations from an existing database and
+generating migration diffs from schema changes.
+
+## Generating Migration Snapshots from an Existing Database
+
+If you have a pre-existing database and want to start using migrations, or want
+to version-control the initial schema of your application, run
+`bake migration_snapshot`:
+
+```bash
+bin/cake bake migration_snapshot Initial
+```
+
+It will generate a migration file called `YYYYMMDDHHMMSS_Initial.php`
+containing all the create statements for all tables in your database.
+
+By default, the snapshot will be created by connecting to the database defined
+in the `default` connection configuration. If you need to bake a snapshot from
+a different datasource, use the `--connection` option:
+
+```bash
+bin/cake bake migration_snapshot Initial --connection my_other_connection
+```
+
+You can also make sure the snapshot includes only the tables for which you have
+defined the corresponding model classes by using the `--require-table` flag:
+
+```bash
+bin/cake bake migration_snapshot Initial --require-table
+```
+
+When using `--require-table`, the shell will look through your application's
+`Table` classes and will only add the model tables to the snapshot.
+
+If you want to generate a snapshot without marking it as migrated, for example
+for use in unit tests, use the `--generate-only` flag:
+
+```bash
+bin/cake bake migration_snapshot Initial --generate-only
+```
+
+This will create the migration file but will not add an entry to the migrations
+tracking table, allowing you to move the file to a different location without
+causing `MISSING` status issues.
+
+To bake a snapshot for a plugin, use the `--plugin` option:
+
+```bash
+bin/cake bake migration_snapshot Initial --plugin MyPlugin
+```
+
+Only the tables that have a `Table` object model class defined will be added to
+the snapshot of your plugin.
+
+> [!NOTE]
+> When baking a snapshot for a plugin, the migration files will be created in
+> your plugin's `config/Migrations` directory.
+
+Be aware that when you bake a snapshot, it is automatically added to the
+migrations log table as migrated unless you use `--generate-only`.
+
+## Generating a Diff
+
+As migrations are applied and rolled back, the migrations plugin will generate
+a dump file of your schema. If you make manual changes to your database schema
+outside of migrations, you can use `bake migration_diff` to generate a
+migration file that captures the difference between the current schema dump file
+and the database schema:
+
+```bash
+bin/cake bake migration_diff NameOfTheMigrations
+```
+
+By default, the diff will be created by connecting to the database defined in
+the `default` connection configuration. If you need to bake a diff from a
+different datasource, use the `--connection` option:
+
+```bash
+bin/cake bake migration_diff NameOfTheMigrations --connection my_other_connection
+```
+
+If you want to use the diff feature on an application that already has a
+migrations history, you need to manually create the dump file that will be used
+as comparison:
+
+```bash
+bin/cake migrations dump
+```
+
+The database state must be the same as it would be if you had just migrated all
+your migrations before you create a dump file. Once the dump file is generated,
+you can start doing changes in your database and use
+`bake migration_diff` whenever you need to capture them.
+
+> [!NOTE]
+> Migration diff generation cannot detect column renamings.
+
+## Generating a Dump File
+
+The dump command creates a file to be used with `bake migration_diff`:
+
+```bash
+bin/cake migrations dump
+```
+
+Each generated dump file is specific to the connection it is generated from,
+and is suffixed as such. This allows `bake migration_diff` to properly compute
+diffs when your application is dealing with multiple databases, possibly from
+different vendors.
+
+Dump files are created in the same directory as your migration files.
+
+You can also use the `--source`, `--connection`, and `--plugin` options just
+like for the `migrate` command.
diff --git a/docs/en/executing-queries.md b/docs/en/guides/executing-queries.md
similarity index 60%
rename from docs/en/executing-queries.md
rename to docs/en/guides/executing-queries.md
index 3c6c3edd3..6f3d605ce 100644
--- a/docs/en/executing-queries.md
+++ b/docs/en/guides/executing-queries.md
@@ -76,60 +76,3 @@ class MyNewMigration extends BaseMigration
}
}
```
-
-## Inserting Data
-
-Migrations makes it easy to insert data into your tables. Whilst this feature is
-intended for the [seed feature](seeding), you are also free to use the
-insert methods in your migrations:
-
-```php
-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(): void
- {
- $this->execute('DELETE FROM status');
- }
-}
-```
-
-> [!NOTE]
-> You cannot use the insert methods inside a change() method. Please use the
-> up() and down() methods.
diff --git a/docs/en/guides/inserting-data.md b/docs/en/guides/inserting-data.md
new file mode 100644
index 000000000..d9661c035
--- /dev/null
+++ b/docs/en/guides/inserting-data.md
@@ -0,0 +1,56 @@
+# Inserting Data
+
+Migrations makes it easy to insert data into your tables. Whilst this feature
+is intended for the [seed feature](seeding), you are also free to use the
+insert methods in your migrations:
+
+```php
+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(): void
+ {
+ $this->execute('DELETE FROM status');
+ }
+}
+```
+
+> [!NOTE]
+> You cannot use insert methods inside `change()`. Use `up()` and `down()`
+> instead.
diff --git a/docs/en/seeding.md b/docs/en/guides/seeding.md
similarity index 100%
rename from docs/en/seeding.md
rename to docs/en/guides/seeding.md
diff --git a/docs/en/using-the-query-builder.md b/docs/en/guides/using-the-query-builder.md
similarity index 100%
rename from docs/en/using-the-query-builder.md
rename to docs/en/guides/using-the-query-builder.md
diff --git a/docs/en/guides/writing-migrations/columns-and-table-operations.md b/docs/en/guides/writing-migrations/columns-and-table-operations.md
new file mode 100644
index 000000000..c8bd82baf
--- /dev/null
+++ b/docs/en/guides/writing-migrations/columns-and-table-operations.md
@@ -0,0 +1,388 @@
+# Columns and Table Operations
+
+
+
+## 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](#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 |
+| length | alias for `limit` |
+| default | set default value or action |
+| null | allow `NULL` values, defaults to `true` |
+| after | specify the column that a new column should be placed after *(MySQL only)* |
+| comment | set a text comment on the column |
+
+For `decimal` and `float` columns:
+
+| Option | Description |
+|-----------|--------------------------------------------------------|
+| precision | total number of digits |
+| scale | number of digits after the decimal point |
+| signed | enable or disable the `unsigned` option *(MySQL only)* |
+
+> [!NOTE]
+> Migrations follows the SQL standard where `precision` is the total number of
+> digits and `scale` is digits after the decimal point.
+
+For `enum` and `set` columns:
+
+| Option | Description |
+|--------|--------------------------------------------|
+| values | comma separated list or an array of values |
+
+For `smallinteger`, `integer` and `biginteger` columns:
+
+| Option | Description |
+|----------|--------------------------------------------------------|
+| identity | enable or disable automatic incrementing |
+| signed | enable or disable the `unsigned` option *(MySQL only)* |
+
+For `date`, `time`, `datetime`, and `timestamp` columns, default/timezone/update
+options are supported as appropriate for the adapter. For `string` and `text`,
+MySQL also supports `collation` and `encoding`.
+
+You can add `created` and `updated` timestamps using `addTimestamps()` or
+`addTimestampsWithTimezone()`:
+
+```php
+$this->table('users')->addTimestamps()->create();
+$this->table('users')->addTimestampsWithTimezone()->create();
+```
+
+### 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.
+
+```php
+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 quoting or escaping:
+
+```php
+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 straightforward using the Table object:
+
+```php
+table('users');
+ $users->addColumn('username', 'string', ['limit' => 20])
+ ->addColumn('password', 'string', ['limit' => 40])
+ ->addColumn('email', 'string', ['limit' => 100])
+ ->addColumn('created', 'datetime')
+ ->addColumn('updated', 'datetime', ['null' => true])
+ ->addIndex(['username', 'email'], ['unique' => true])
+ ->create();
+ }
+}
+```
+
+Migrations automatically creates an auto-incrementing primary key column called
+`id` for every table unless configured otherwise.
+
+You can disable the automatic `id` column and define a custom primary key:
+
+```php
+$table = $this->table('followers', [
+ 'id' => false,
+ 'primary_key' => ['user_id', 'follower_id'],
+]);
+```
+
+The MySQL adapter also supports table options such as `comment`, `collation`,
+`row_format`, `engine`, `signed`, and `limit`.
+
+To set unsigned primary keys, pass the `signed` option with `false`, or enable
+the `Migrations.unsigned_primary_keys` and `Migrations.unsigned_ints` feature
+flags together. See [Feature Flags](../../advanced/integration-and-deployment#feature-flags).
+
+## MySQL ALTER TABLE Options
+
+When modifying tables in MySQL, you can control how the `ALTER TABLE`
+operation is performed using the `algorithm` and `lock` options:
+
+```php
+$table->update([
+ 'algorithm' => 'INPLACE',
+ 'lock' => 'NONE',
+]);
+```
+
+Not all operations support all `algorithm` and `lock` combinations, and MySQL
+will raise an error if the requested combination is not possible.
+
+## Table Partitioning
+
+Migrations supports table partitioning for MySQL and PostgreSQL. Partitioning
+helps manage large tables by splitting them into smaller, more manageable
+pieces.
+
+Supported strategies include:
+
+- `RANGE`
+- `RANGE COLUMNS`
+- `LIST`
+- `LIST COLUMNS`
+- `HASH`
+- `KEY` *(MySQL only)*
+
+You can also partition by expressions using `Literal::from(...)`, and add or
+drop partitions on existing tables.
+
+## 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. Migrations provides three methods:
+
+- `create()` creates the table and runs pending changes
+- `update()` runs pending changes on an existing table
+- `save()` creates or updates depending on whether the table exists
+
+When using `change()`, you should always use `create()` or `update()`, and
+never `save()`.
+
+## Renaming a Column
+
+To rename a column, call `renameColumn()`:
+
+```php
+$this->table('users')
+ ->renameColumn('bio', 'biography')
+ ->save();
+```
+
+## Adding a Column After Another Column
+
+When adding a column with the MySQL adapter, you can dictate its position using
+the `after` option:
+
+```php
+$this->table('users')
+ ->addColumn('city', 'string', ['after' => 'email'])
+ ->update();
+```
+
+The `\Migrations\Db\Adapter\MysqlAdapter::FIRST` constant can be used to place
+the new column first.
+
+## Dropping a Column
+
+To drop a column, use `removeColumn()`:
+
+```php
+$this->table('users')
+ ->removeColumn('short_name')
+ ->save();
+```
+
+## Specifying a Column Limit
+
+You can limit the maximum length of a column using the `limit` option:
+
+```php
+$this->table('tags')
+ ->addColumn('short_name', 'string', ['limit' => 30])
+ ->update();
+```
+
+## Changing Column Attributes
+
+There are two methods for modifying existing columns:
+
+### Updating Columns
+
+To modify specific column attributes while preserving others, use
+`updateColumn()`:
+
+```php
+$this->table('users')
+ ->updateColumn('email', null, ['null' => true])
+ ->save();
+```
+
+This automatically preserves unspecified attributes such as defaults,
+nullability, limits, comments, signedness, collation, and enum/set values.
+
+### Changing Columns
+
+To completely replace a column definition, use `changeColumn()` and specify all
+desired attributes:
+
+```php
+$this->table('users')
+ ->changeColumn('email', 'string', [
+ 'limit' => 255,
+ 'null' => true,
+ 'default' => null,
+ ])
+ ->save();
+```
+
+You can enable attribute preservation with `'preserveUnspecified' => true`.
+
+## Determining Whether a Table Exists
+
+Use `hasTable()` to check whether a table exists:
+
+```php
+if ($this->hasTable('users')) {
+ // do something
+}
+```
+
+## Dropping a Table
+
+Tables can be dropped using `drop()`:
+
+```php
+$this->table('users')->drop()->save();
+```
+
+## Renaming a Table
+
+To rename a table, call `rename()`:
+
+```php
+$this->table('users')
+ ->rename('legacy_users')
+ ->update();
+```
+
+## Changing the Primary Key
+
+To change the primary key on an existing table, use `changePrimaryKey()`:
+
+```php
+$this->table('users')
+ ->changePrimaryKey(['new_id', 'username'])
+ ->save();
+```
+
+## Creating Custom Primary Keys
+
+You can specify an `autoId` property in the migration class and set it to
+`false`, which turns off automatic `id` column creation:
+
+```php
+table('products')
+ ->addColumn('id', 'uuid')
+ ->addPrimaryKey('id')
+ ->addColumn('name', 'string')
+ ->addColumn('description', 'text')
+ ->create();
+ }
+}
+```
+
+> [!WARNING]
+> Dealing with primary keys can only be done on table creation operations for
+> some database servers.
+
+## Changing the Table Comment
+
+To change the comment on an existing table, use `changeComment()`:
+
+```php
+$this->table('users')
+ ->changeComment('This is the table with users auth information')
+ ->save();
+```
+
+## Next Steps
+
+- [Indexes and Constraints](indexes-and-constraints)
+- [Schema Introspection and Platform Limitations](schema-introspection-and-platform-limitations)
diff --git a/docs/en/guides/writing-migrations/index.md b/docs/en/guides/writing-migrations/index.md
new file mode 100644
index 000000000..7d25425c4
--- /dev/null
+++ b/docs/en/guides/writing-migrations/index.md
@@ -0,0 +1,21 @@
+# Writing Migrations
+
+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.
+
+For generating migration files with `bake`, naming patterns, anonymous
+migration classes, and command-line column syntax, see
+[Creating Migrations](../../getting-started/creating-migrations).
+
+## Guide Map
+
+This section has been split into focused reference pages:
+
+- [Migration Methods](migration-methods)
+- [Columns and Table Operations](columns-and-table-operations)
+- [Indexes and Constraints](indexes-and-constraints)
+- [Schema Introspection and Platform Limitations](schema-introspection-and-platform-limitations)
+
+Use these pages as the API reference for authoring migrations.
diff --git a/docs/en/guides/writing-migrations/indexes-and-constraints.md b/docs/en/guides/writing-migrations/indexes-and-constraints.md
new file mode 100644
index 000000000..5c7a680db
--- /dev/null
+++ b/docs/en/guides/writing-migrations/indexes-and-constraints.md
@@ -0,0 +1,148 @@
+# Indexes and Constraints
+
+## Working With Indexes
+
+To add an index to a table, call `addIndex()`:
+
+```php
+table('users')
+ ->addColumn('city', 'string')
+ ->addIndex(['city'])
+ ->save();
+ }
+}
+```
+
+You can also specify unique indexes, explicit names, sort order, index length,
+and advanced adapter-specific options.
+
+The fluent builder is also available:
+
+```php
+$this->table('users')
+ ->addIndex(
+ $this->index(['email', 'username'])
+ ->setType('unique')
+ ->setName('idx_users_email')
+ ->setOrder(['email' => 'DESC', 'username' => 'ASC'])
+ )
+ ->save();
+```
+
+Adapter-specific capabilities include:
+
+- MySQL `fulltext` indexes
+- MySQL index-length options
+- SQL Server and PostgreSQL `include` columns
+- PostgreSQL, SQL Server, and SQLite partial indexes
+- PostgreSQL concurrent index creation
+- PostgreSQL `gin` indexes
+
+To remove indexes, use `removeIndex()` or `removeIndexByName()`.
+
+## Working With Foreign Keys
+
+Migrations supports foreign key constraints on database tables:
+
+```php
+table('tag_relationships')
+ ->addColumn('tag_id', 'integer', ['null' => true])
+ ->addForeignKey(
+ 'tag_id',
+ 'tags',
+ 'id',
+ ['delete' => 'SET_NULL', 'update' => 'NO_ACTION']
+ )
+ ->save();
+ }
+}
+```
+
+The `delete` and `update` options control `ON DELETE` and `ON UPDATE`
+behavior. Valid values are `SET_NULL`, `NO_ACTION`, `CASCADE`, and
+`RESTRICT`.
+
+Foreign keys can also be defined with arrays of columns for composite keys.
+
+The `foreignKey()` fluent builder is available for more complex cases:
+
+```php
+$this->table('articles')
+ ->addForeignKey(
+ $this->foreignKey()
+ ->setColumns('user_id')
+ ->setReferencedTable('users')
+ ->setReferencedColumns('user_id')
+ ->setName('article_user_fk')
+ )
+ ->save();
+```
+
+Use `hasForeignKey()` to check whether a foreign key exists, and
+`dropForeignKey()` to remove one.
+
+## Working With Check Constraints
+
+Check constraints allow you to enforce data validation rules at the database
+level.
+
+> [!NOTE]
+> Check constraints are supported by MySQL 8.0.16+, PostgreSQL, and SQLite.
+
+### Adding a Check Constraint
+
+```php
+$this->table('products')
+ ->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])
+ ->addCheckConstraint('price_positive', 'price > 0')
+ ->save();
+```
+
+### Using the Fluent Builder
+
+```php
+$this->table('users')
+ ->addCheckConstraint(
+ $this->checkConstraint()
+ ->setName('age_valid')
+ ->setExpression('age >= 18 AND age <= 120')
+ )
+ ->save();
+```
+
+If you do not specify a name, one will be auto-generated.
+
+Check constraints can reference multiple columns and use more complex SQL
+expressions.
+
+Use `hasCheckConstraint()` to verify existence and `dropCheckConstraint()` to
+remove a constraint.
+
+### Database-Specific Behavior
+
+- MySQL stores check constraint metadata in
+ `INFORMATION_SCHEMA.CHECK_CONSTRAINTS`
+- PostgreSQL stores constraints in `pg_constraint`
+- SQLite recreates the table when altering check constraints
+- SQL Server support is planned for a future release
+
+## Next Steps
+
+- [Columns and Table Operations](columns-and-table-operations)
+- [Schema Introspection and Platform Limitations](schema-introspection-and-platform-limitations)
diff --git a/docs/en/guides/writing-migrations/migration-methods.md b/docs/en/guides/writing-migrations/migration-methods.md
new file mode 100644
index 000000000..2ac07c995
--- /dev/null
+++ b/docs/en/guides/writing-migrations/migration-methods.md
@@ -0,0 +1,135 @@
+# Migration Methods
+
+## The Change Method
+
+Migrations supports 'reversible migrations'. In many scenarios, you only need
+to define the `up` logic, and Migrations can figure out how to generate the
+rollback operations for you. For example:
+
+```php
+table('user_logins');
+ $table->addColumn('user_id', 'integer')
+ ->addColumn('created', 'datetime')
+ ->create();
+ }
+}
+```
+
+When executing this migration, Migrations 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, Migrations will
+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. Migrations 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
+Migrations, and will be automatically reversed:
+
+- Creating a table
+- Renaming a table
+- Adding a column
+- Renaming a column
+- Adding an index
+- Adding a foreign key
+- Adding a check constraint
+
+If a command cannot be reversed then Migrations 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:
+
+```php
+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 Migrations 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 Migrations 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 the transformations described in the
+`up()` method.
+
+## The Init Method
+
+The `init()` method is run by Migrations 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 Migrations 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 `BaseMigration` implementation.
+
+## Working With Tables
+
+The Table object enables you to 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:
+
+```php
+table('tableName');
+ }
+
+ public function down(): void
+ {
+ }
+}
+```
+
+You can then manipulate this table using the methods provided by the Table
+object.
+
+## Next Steps
+
+- [Columns and Table Operations](columns-and-table-operations)
+- [Indexes and Constraints](indexes-and-constraints)
+- [Schema Introspection and Platform Limitations](schema-introspection-and-platform-limitations)
diff --git a/docs/en/guides/writing-migrations/schema-introspection-and-platform-limitations.md b/docs/en/guides/writing-migrations/schema-introspection-and-platform-limitations.md
new file mode 100644
index 000000000..d733a9c9d
--- /dev/null
+++ b/docs/en/guides/writing-migrations/schema-introspection-and-platform-limitations.md
@@ -0,0 +1,105 @@
+# Schema Introspection and Platform Limitations
+
+## Checking Columns
+
+`BaseMigration` also provides methods for introspecting the current schema,
+allowing you to conditionally make changes to schema, or read data. Schema is
+inspected when the migration is run.
+
+### Get a Column List
+
+To retrieve all table columns, create a table object and call `getColumns()`:
+
+```php
+table('users')->getColumns();
+ }
+}
+```
+
+### Get a Column by Name
+
+To retrieve one table column, call `getColumn()`:
+
+```php
+table('users')->getColumn('email');
+ }
+}
+```
+
+### Check Whether a Column Exists
+
+Use `hasColumn()` to determine whether a table already has a given column:
+
+```php
+table('user');
+ if ($table->hasColumn('username')) {
+ // do something
+ }
+ }
+}
+```
+
+## Changing Templates
+
+See [Custom Seed Migration Templates](../seeding#custom-seed-migration-templates)
+for how to customize the templates used to generate migrations.
+
+## Database-Specific Limitations
+
+While Migrations aims to provide a database-agnostic API, some features have
+database-specific limitations or are not available on all platforms.
+
+### SQL Server
+
+The following features are not supported on SQL Server:
+
+- Check constraints are not currently implemented
+- Table comments are not supported
+- `insertOrSkip()` is not supported; use `insertOrUpdate()` instead
+
+### SQLite
+
+SQLite limitations include:
+
+- named foreign keys are not supported
+- table comments are stored as metadata, not in the database itself
+- altering check constraints requires recreating the table
+- table partitioning is not supported
+
+### PostgreSQL
+
+PostgreSQL does not support MySQL's `KEY` partitioning type. Use `HASH`
+partitioning instead for similar distribution behavior.
+
+### MySQL/MariaDB
+
+For MySQL, the `$conflictColumns` parameter in `insertOrUpdate()` is ignored
+because MySQL's `ON DUPLICATE KEY UPDATE` automatically applies to all unique
+constraints. PostgreSQL and SQLite require this parameter to be specified.
+
+Some geometry column features may not work correctly on MariaDB due to
+differences in GIS implementation compared to MySQL.
diff --git a/docs/en/index.md b/docs/en/index.md
index 2126603b6..7c5c13c0d 100644
--- a/docs/en/index.md
+++ b/docs/en/index.md
@@ -14,20 +14,22 @@ Migrations 5.x includes several new features:
- **Seed tracking** - Seeds are now tracked in a `cake_seeds` table, preventing
accidental re-runs
-- **Check constraints** - Support for database check constraints via `addCheckConstraint()`
+- **Check constraints** - Support for database check constraints via
+ `addCheckConstraint()`
- **Default values in bake** - Specify default values when baking migrations
(e.g., `active:boolean:default[true]`)
-- **MySQL ALTER options** - Control `ALGORITHM` and `LOCK` for ALTER TABLE operations
+- **MySQL ALTER options** - Control `ALGORITHM` and `LOCK` for ALTER TABLE
+ operations
- **insertOrSkip()** - New method for idempotent seed data insertion
-See the [Upgrading from 4.x to 5.x](upgrading) guide for breaking changes and migration steps from 4.x.
+See the [Upgrading from 4.x to 5.x](upgrades/upgrading-from-4-x) guide for breaking changes and
+migration steps from 4.x.
## Installation
-By default Migrations is installed with the application skeleton. If
-you've removed it and want to re-install it, you can do so by running the
-following from your application's ROOT directory (where **composer.json** file is
-located):
+By default Migrations is installed with the application skeleton. If you've
+removed it and want to re-install it, run the following from your application's
+root directory:
```bash
php composer.phar require cakephp/migrations "@stable"
@@ -36,34 +38,26 @@ php composer.phar require cakephp/migrations "@stable"
composer require cakephp/migrations "@stable"
```
-To use the plugin you'll need to load it in your application's
-**config/bootstrap.php** file. You can use [CakePHP's Plugin shell](https://book.cakephp.org/5/en/console-and-shells/plugin-shell.html) to
-load and unload plugins from your **config/bootstrap.php**:
+To use the plugin, load it in your application's `config/bootstrap.php` file:
```bash
bin/cake plugin load Migrations
```
-Or you can load the plugin by editing your **src/Application.php** file and
-adding the following statement:
+Or load the plugin in `src/Application.php`:
```php
$this->addPlugin('Migrations');
```
-Additionally, you will need to configure the default database configuration for
-your application in your **config/app.php** file as explained in the [Database
-Configuration section](https://book.cakephp.org/5/en/orm/database-basics.html#database-configuration).
-
-## Upgrading from 4.x
-
-If you are upgrading from Migrations 4.x, please see the [Upgrading from 4.x to 5.x](upgrading) guide
-for breaking changes and migration steps.
+Additionally, configure the default database connection in `config/app.php` as
+explained in the [Database Configuration section](https://book.cakephp.org/5/en/orm/database-basics.html#database-configuration).
## Overview
A migration is a PHP file that describes the changes to apply to your database.
-A migration file can add, change or remove tables, columns, indexes and foreign keys.
+A migration file can add, change, or remove tables, columns, indexes, and
+foreign keys.
If we wanted to create a table, we could use a migration similar to this:
@@ -109,833 +103,114 @@ When applied, this migration will add a table to your database named
- `modified` column of type `datetime`
> [!NOTE]
-> Migrations are not automatically applied, you can apply and rollback
-> migrations with CLI commands.
-
-Once the file has been created in the **config/Migrations** folder, you can
-apply it:
+> Migrations are not automatically applied. Use the CLI commands to apply and
+> roll back migrations.
```bash
bin/cake migrations migrate
```
-## Creating Migrations
-
-Migration files are stored in the **config/Migrations** directory of your
-application. The name of the migration files are prefixed with the date in
-which they were created, in the format **YYYYMMDDHHMMSS_MigrationName.php**.
-Here are examples of migration filenames:
-
-- **20160121163850_CreateProducts.php**
-- **20160210133047_AddRatingToProducts.php**
-
-The easiest way to create a migrations file is by using `bin/cake bake migration` CLI command:
-
-```bash
-bin/cake bake migration CreateProducts
-```
-
-This will create an empty migration that you can edit to add any columns,
-indexes and foreign keys you need. See the [Creating A Table](writing-migrations#creating-a-table) section to
-learn more about using migrations to define tables.
-
-> [!NOTE]
-> Migrations need to be applied using `bin/cake migrations migrate` after
-> they have been created.
-
-### Migration file names
-
-When generating a migration, you can follow one of the following patterns
-to have additional skeleton code generated:
-
-- `/^(Create)(.*)/` Creates the specified table.
-- `/^(Drop)(.*)/` Drops the specified table.
- Ignores specified field arguments
-- `/^(Add).*(?:To)(.*)/` Adds fields to the specified
- table
-- `/^(Remove).*(?:From)(.*)/` Removes fields from the
- specified table
-- `/^(Alter)(.*)/` Alters the specified table. An alias
- for CreateTable and AddField.
-- `/^(Alter).*(?:On)(.*)/` Alters fields from the specified table.
-
-You can also use the `underscore_form` as the name for your migrations i.e.
-`create_products`.
-
-> [!WARNING]
-> Migration names are used as class names, and thus may collide with
-> other migrations if the class names are not unique. In this case, it may be
-> necessary to manually override the name at a later date, or simply change
-> the name you are specifying.
-
-### Creating a table
-
-You can use `bake migration` to create a table:
-
-```bash
-bin/cake bake migration CreateProducts name:string description:text created modified
-```
-
-The command line above will generate a migration file that resembles:
-
-```php
-table('products');
- $table->addColumn('name', 'string', [
- 'default' => null,
- 'limit' => 255,
- 'null' => false,
- ]);
- $table->addColumn('description', 'text', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('created', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('modified', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->create();
- }
-}
-```
-
-### Column syntax
-
-The `bake migration` command provides a compact syntax to define columns when
-generating a migration:
-
-```bash
-bin/cake bake migration CreateProducts name:string description:text created modified
-```
-
-You can use the column syntax when creating tables and adding columns. You can
-also edit the migration after generation to add or customize the columns
-
-Columns on the command line follow the following pattern:
-
- fieldName:fieldType?[length]:default[value]:indexType:indexName
-
-For instance, the following are all valid ways of specifying an email field:
-
-- `email:string?`
-- `email:string:unique`
-- `email:string?[50]`
-- `email:string:unique:EMAIL_INDEX`
-- `email:string[120]:unique:EMAIL_INDEX`
-
-While defining decimal columns, the `length` can be defined to have precision
-and scale, separated by a comma.
-
-- `amount:decimal[5,2]`
-- `amount:decimal?[5,2]`
-
-Columns with a question mark after the fieldType will make the column nullable.
-
-The `length` part is optional and should always be written between bracket.
-
-The `default[value]` part is optional and sets the default value for the column.
-Supported value types include:
-
-- Booleans: `true` or `false` - e.g., `active:boolean:default[true]`
-- Integers: `0`, `123`, `-456` - e.g., `count:integer:default[0]`
-- Floats: `1.5`, `-2.75` - e.g., `rate:decimal:default[1.5]`
-- Strings: `'hello'` or `"world"` (quoted) - e.g., `status:string:default['pending']`
-- Null: `null` or `NULL` - e.g., `description:text?:default[null]`
-- SQL expressions: `CURRENT_TIMESTAMP` - e.g., `created_at:datetime:default[CURRENT_TIMESTAMP]`
-
-Fields named `created` and `modified`, as well as any field with a `_at`
-suffix, will automatically be set to the type `datetime`.
-
-There are some heuristics to choosing fieldtypes when left unspecified or set to
-an invalid value. Default field type is `string`:
-
-- id: integer
-- created, modified, updated: datetime
-- latitude, longitude (or short forms lat, lng): decimal
-
-Additionally you can create an empty migrations file if you want full control
-over what needs to be executed, by omitting to specify a columns definition:
-
-```bash
-bin/cake migrations create MyCustomMigration
-```
-
-See [Writing Migrations](writing-migrations) for more information on how to use `Table`
-objects to interact with tables and define schema changes.
-
-### Adding columns to an existing table
-
-If the migration name in the command line is of the form "AddXXXToYYY" and is
-followed by a list of column names and types then a migration file containing
-the code for creating the columns will be generated:
-
-```bash
-bin/cake bake migration AddPriceToProducts price:decimal[5,2]
-```
-
-Executing the command line above will generate:
-
-```php
-table('products');
- $table->addColumn('price', 'decimal', [
- 'default' => null,
- 'null' => false,
- 'precision' => 5,
- 'scale' => 2,
- ]);
- $table->update();
- }
-}
-```
-
-### Adding a column with an index
-
-It is also possible to add indexes to columns:
-
-```bash
-bin/cake bake migration AddNameIndexToProducts name:string:index
-```
-
-will generate:
-
-```php
-table('products');
- $table->addColumn('name', 'string')
- ->addColumn('email', 'string')
- ->addIndex(['name'])
- // add a unique index:
- ->addIndex('email', ['unique' => true])
- ->update();
- }
-}
-```
-
-### Adding a column with a default value
+## Guide Map
-You can specify default values for columns using the `default[value]` syntax:
+Use the focused guides below instead of a single long reference page:
-```bash
-bin/cake bake migration AddActiveToUsers active:boolean:default[true]
-```
-
-will generate:
+- [Installation and Overview](getting-started/installation-and-overview)
+- [Creating Migrations](getting-started/creating-migrations)
+- [Snapshots and Diffs](getting-started/snapshots-and-diffs)
+- [Running and Managing Migrations](getting-started/running-and-managing-migrations)
+- [Integration and Deployment](advanced/integration-and-deployment)
+- [Writing Migrations](guides/writing-migrations)
+- [Using the Query Builder](guides/using-the-query-builder)
+- [Executing Queries](guides/executing-queries)
+- [Database Seeding](guides/seeding)
+- [Upgrading to the builtin backend](upgrades/upgrading-to-builtin-backend)
-```php
-table('users');
- $table->addColumn('active', 'boolean', [
- 'default' => true,
- 'null' => false,
- ]);
- $table->update();
- }
-}
-```
-
-You can combine default values with other options like nullable and indexes:
-
-```bash
-bin/cake bake migration AddStatusToOrders status:string:default['pending']:unique
-```
-
-### Altering a column
-
-In the same way, you can generate a migration to alter a column by using the
-command line, if the migration name is of the form "AlterXXXOnYYY":
-
-```bash
-bin/cake bake migration AlterPriceOnProducts name:float
-```
-
-will generate:
-
-```php
-table('products');
- $table->changeColumn('name', 'float');
- $table->update();
- }
-}
-```
-
-> [!WARNING]
-> Changing the type of a column can result in data loss if the
-> current and target column type are not compatible. For example converting
-> a varchar to float.
-
-### Removing a column
-
-In the same way, you can generate a migration to remove a column by using the
-command line, if the migration name is of the form "RemoveXXXFromYYY":
-
-```bash
-bin/cake bake migration RemovePriceFromProducts price
-```
-
-creates the file:
+## Upgrading from 4.x
-```php
-table('products');
- $table->removeColumn('price')
- ->save();
- }
-}
-```
+## Creating Migrations
-> [!NOTE]
-> The removeColumn command is not reversible, so must be called in the
-> up method. A corresponding addColumn call should be added to the
-> down method.
+Migration naming conventions, bake patterns, column syntax, and generated
+examples are covered in [Creating Migrations](getting-started/creating-migrations).
## Generating migration snapshots from an existing database
-If you have a pre-existing database and want to start using
-migrations, or to version control the initial schema of your application's
-database, you can run the `bake migration_snapshot` command:
-
-```bash
-bin/cake bake migration_snapshot Initial
-```
-
-It will generate a migration file called **YYYYMMDDHHMMSS_Initial.php**
-containing all the create statements for all tables in your database.
-
-By default, the snapshot will be created by connecting to the database defined
-in the `default` connection configuration. If you need to bake a snapshot from
-a different datasource, you can use the `--connection` option:
-
-```bash
-bin/cake bake migration_snapshot Initial --connection my_other_connection
-```
-
-You can also make sure the snapshot includes only the tables for which you have
-defined the corresponding model classes by using the `--require-table` flag:
-
-```bash
-bin/cake bake migration_snapshot Initial --require-table
-```
-
-When using the `--require-table` flag, the shell will look through your
-application `Table` classes and will only add the model tables in the snapshot.
-
-If you want to generate a snapshot without marking it as migrated (for example,
-for use in unit tests), you can use the `--generate-only` flag:
-
-```bash
-bin/cake bake migration_snapshot Initial --generate-only
-```
-
-This will create the migration file but will not add an entry to the migrations
-tracking table, allowing you to move the file to a different location without causing
-"MISSING" status issues.
-
-The same logic will be applied implicitly if you wish to bake a snapshot for a
-plugin. To do so, you need to use the `--plugin` option:
-
-```bash
-bin/cake bake migration_snapshot Initial --plugin MyPlugin
-```
-
-Only the tables which have a `Table` object model class defined will be added
-to the snapshot of your plugin.
-
-> [!NOTE]
-> When baking a snapshot for a plugin, the migration files will be created
-> in your plugin's **config/Migrations** directory.
-
-Be aware that when you bake a snapshot, it is automatically added to the
-migrations log table as migrated.
+For snapshot generation, diff workflows, and dump files, see
+[Snapshots and Diffs](getting-started/snapshots-and-diffs).
## Generating a diff
-As migrations are applied and rolled back, the migrations plugin will generate
-a 'dump' file of your schema. If you make manual changes to your database schema
-outside of migrations, you can use `bake migration_diff` to generate
-a migration file that captures the difference between the current schema dump
-file and database schema. To do so, you can use the following command:
-
-```bash
-bin/cake bake migration_diff NameOfTheMigrations
-```
-
-By default, the diff will be created by connecting to the database defined
-in the `default` connection configuration.
-If you need to bake a diff from a different datasource, you can use the
-`--connection` option:
-
-```bash
-bin/cake bake migration_diff NameOfTheMigrations --connection my_other_connection
-```
-
-If you want to use the diff feature on an application that already has a
-migrations history, you need to manually create the dump file that will be used
-as comparison:
-
-```bash
-bin/cake migrations dump
-```
-
-The database state must be the same as it would be if you just migrated all
-your migrations before you create a dump file.
-Once the dump file is generated, you can start doing changes in your database
-and use the `bake migration_diff` command whenever you see fit.
-
-> [!NOTE]
-> Migration diff generation can not detect column renamings.
+The `bake migration_diff` workflow is also covered in
+[Snapshots and Diffs](getting-started/snapshots-and-diffs).
## Applying Migrations
-Once you have generated or written your migration file, you need to execute the
-following command to apply the changes to your database:
-
-```bash
-# Run all the migrations
-bin/cake migrations migrate
-
-# Migrate to a specific version using the ``--target`` option
-# or ``-t`` for short.
-# The value is the timestamp that is prefixed to the migrations file name::
-bin/cake migrations migrate -t 20150103081132
-
-# By default, migration files are looked for in the **config/Migrations**
-# directory. You can specify the directory using the ``--source`` option
-# or ``-s`` for short.
-# The following example will run migrations in the **config/Alternate**
-# directory
-bin/cake migrations migrate -s Alternate
-
-# You can run migrations to a different connection than the ``default`` one
-# using the ``--connection`` option or ``-c`` for short
-bin/cake migrations migrate -c my_custom_connection
-
-# Migrations can also be run for plugins. Simply use the ``--plugin`` option
-# or ``-p`` for short
-bin/cake migrations migrate -p MyAwesomePlugin
-```
+For `migrate`, `rollback`, `status`, `mark_migrated`, and related command
+options, see
+[Running and Managing Migrations](getting-started/running-and-managing-migrations).
## Reverting Migrations
-The rollback command is used to undo previous migrations executed by this
-plugin. It is the reverse action of the `migrate` command:
-
-```bash
-# You can rollback to the previous migration by using the
-# ``rollback`` command::
-bin/cake migrations rollback
-
-# You can also pass a migration version number to rollback
-# to a specific version::
-bin/cake migrations rollback -t 20150103081132
-```
-
-You can also use the `--source`, `--connection` and `--plugin` options
-just like for the `migrate` command.
+Rollback usage is documented in
+[Running and Managing Migrations](getting-started/running-and-managing-migrations).
## View Migrations Status
-The Status command prints a list of all migrations, along with their current
-status. You can use this command to determine which migrations have been run:
-
-```bash
-bin/cake migrations status
-```
-
-You can also output the results as a JSON formatted string using the
-`--format` option (or `-f` for short):
-
-```bash
-bin/cake migrations status --format json
-```
-
-You can also use the `--source`, `--connection` and `--plugin` options
-just like for the `migrate` command.
-
-### Cleaning up missing migrations
-
-Sometimes migration files may be deleted from the filesystem but still exist
-in the migrations tracking table. These migrations will be marked as **MISSING** in the
-status output. You can remove these entries from the tracking table using the
-`--cleanup` option:
-
-```bash
-bin/cake migrations status --cleanup
-```
-
-This will remove all migration entries from the tracking table that no longer
-have corresponding migration files in the filesystem.
+Status commands, cleanup, and JSON output are documented in
+[Running and Managing Migrations](getting-started/running-and-managing-migrations).
## Marking a migration as migrated
-It can sometimes be useful to mark a set of migrations as migrated without
-actually running them. In order to do this, you can use the `mark_migrated`
-command. The command works seamlessly as the other commands.
-
-You can mark all migrations as migrated using this command:
-
-```bash
-bin/cake migrations mark_migrated
-```
-
-You can also mark all migrations up to a specific version as migrated using
-the `--target` option:
-
-```bash
-bin/cake migrations mark_migrated --target=20151016204000
-```
-
-If you do not want the targeted migration to be marked as migrated during the
-process, you can use the `--exclude` flag with it:
-
-```bash
-bin/cake migrations mark_migrated --target=20151016204000 --exclude
-```
-
-Finally, if you wish to mark only the targeted migration as migrated, you can
-use the `--only` flag:
-
-```bash
-bin/cake migrations mark_migrated --target=20151016204000 --only
-```
-
-You can also use the `--source`, `--connection` and `--plugin` options
-just like for the `migrate` command.
-
-> [!NOTE]
-> When you bake a snapshot with the `cake bake migration_snapshot`
-> command, the created migration will automatically be marked as migrated.
-> To prevent this behavior (e.g., for unit test migrations), use the
-> `--generate-only` flag.
-
-This command expects the migration version number as argument:
-
-```bash
-bin/cake migrations mark_migrated 20150420082532
-```
-
-If you wish to mark all migrations as migrated, you can use the `all` special
-value. If you use it, it will mark all found migrations as migrated:
-
-```bash
-bin/cake migrations mark_migrated all
-```
+See [Running and Managing Migrations](getting-started/running-and-managing-migrations)
+for `mark_migrated` examples and caveats.
## Seeding your database
-Seed classes are a good way to populate your database with default or starter
-data. They are also a great way to generate data for development environments.
-
-By default, seeds will be looked for in the `config/Seeds/` directory of
-your application. See the [Database Seeding](seeding) for how to build and use seed classes.
+Seed classes are documented in [Database Seeding](guides/seeding).
## Generating a dump file
-The dump command creates a file to be used with the `bake migration_diff`
-command:
-
-```bash
-bin/cake migrations dump
-```
-
-Each generated dump file is specific to the Connection it is generated from (and
-is suffixed as such). This allows the `bake migration_diff` command to
-properly compute diff in case your application is dealing with multiple database
-possibly from different database vendors.
-
-Dump files are created in the same directory as your migrations files.
-
-You can also use the `--source`, `--connection` and `--plugin` options
-just like for the `migrate` command.
+Dump generation is documented in
+[Snapshots and Diffs](getting-started/snapshots-and-diffs).
## Using Migrations for Tests
-If you are using migrations for your application schema you can also use those
-same migrations to build schema in your tests. In your application's
-`tests/bootstrap.php` file you can use the `Migrator` class to build schema
-when tests are run. The `Migrator` will use existing schema if it is current,
-and if the migration history that is in the database differs from what is in the
-filesystem, all tables will be dropped and migrations will be rerun from the
-beginning:
-
-```php
-// in tests/bootstrap.php
-use Migrations\TestSuite\Migrator;
-
-$migrator = new Migrator();
-
-// Simple setup for with no plugins
-$migrator->run();
-
-// Run a non 'test' database
-$migrator->run(['connection' => 'test_other']);
-
-// Run migrations for plugins
-$migrator->run(['plugin' => 'Contacts']);
-
-// Run the Documents migrations on the test_docs connection.
-$migrator->run(['plugin' => 'Documents', 'connection' => 'test_docs']);
-```
-
-If you need to run multiple sets of migrations, those can be run as follows:
-
-```php
-// Run migrations for plugin Contacts on the ``test`` connection, and Documents on the ``test_docs`` connection
-$migrator->runMany([
- ['plugin' => 'Contacts'],
- ['plugin' => 'Documents', 'connection' => 'test_docs']
-]);
-```
-
-If your database also contains tables that are not managed by your application
-like those created by PostGIS, then you can exclude those tables from the drop
-& truncate behavior using the `skip` option:
-
-```php
-$migrator->run(['connection' => 'test', 'skip' => ['postgis*']]);
-```
-
-The `skip` option accepts a `fnmatch()` compatible pattern to exclude tables
-from drop & truncate operations.
-
-If you need to see additional debugging output from migrations are being run,
-you can enable a `debug` level logger.
+Test bootstrapping with `Migrations\TestSuite\Migrator` is covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Using Migrations In Plugins
-Plugins can also provide migration files. This makes plugins that are intended
-to be distributed much more portable and easy to install. All commands in the
-Migrations plugin support the `--plugin` or `-p` option that will scope the
-execution to the migrations relative to that plugin:
-
-```bash
-bin/cake migrations status -p PluginName
-
-bin/cake migrations migrate -p PluginName
-```
+Plugin-scoped migration workflows are covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Running Migrations in a non-shell environment
-While typical usage of migrations is from the command line, you can also run
-migrations from a non-shell environment, by using
-`Migrations\Migrations` class. This can be handy in case you are developing a plugin
-installer for a CMS for instance. The `Migrations` class allows you to run the
-following commands from the migrations shell:
-
-- migrate
-- rollback
-- markMigrated
-- status
-- seed
-
-Each of these commands has a method defined in the `Migrations` class.
-
-Here is how to use it:
-
-```php
-use Migrations\Migrations;
-
-$migrations = new Migrations();
-
-// Will return an array of all migrations and their status
-$status = $migrations->status();
-
-// Will return true if success. If an error occurred, an exception will be thrown
-$migrate = $migrations->migrate();
-
-// Will return true if success. If an error occurred, an exception will be thrown
-$rollback = $migrations->rollback();
-
-// Will return true if success. If an error occurred, an exception will be thrown
-$markMigrated = $migrations->markMigrated(20150804222900);
-
-// Will return true if success. If an error occurred, an exception will be thrown
-$seeded = $migrations->seed();
-```
-
-The methods can accept an array of parameters that should match options from
-the commands:
-
-```php
-use Migrations\Migrations;
-
-$migrations = new Migrations();
-
-// Will return an array of all migrations and their status
-$status = $migrations->status(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-```
-
-You can pass any options the shell commands would take.
-The only exception is the `markMigrated` command which is expecting the
-version number of the migrations to mark as migrated as first argument. Pass
-the array of parameters as the second argument for this method.
-
-Optionally, you can pass these parameters in the constructor of the class.
-They will be used as default and this will prevent you from having to pass
-them on each method call:
-
-```php
-use Migrations\Migrations;
-
-$migrations = new Migrations(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-
-// All the following calls will be done with the parameters passed to the Migrations class constructor
-$status = $migrations->status();
-$migrate = $migrations->migrate();
-```
-
-If you need to override one or more default parameters for one call, you can
-pass them to the method:
-
-```php
-use Migrations\Migrations;
-
-$migrations = new Migrations(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-
-// This call will be made with the "custom" connection
-$status = $migrations->status();
-// This one with the "default" connection
-$migrate = $migrations->migrate(['connection' => 'default']);
-```
-
-
+Programmatic execution through `Migrations\Migrations` is covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Feature Flags
-Migrations offers a few feature flags for compatibility. These features are disabled by default but can be enabled if required:
-
-- `unsigned_primary_keys`: Should Migrations create primary keys as unsigned integers? (default: `false`)
-- `unsigned_ints`: Should Migrations create all integer columns as unsigned? (default: `false`)
-- `column_null_default`: Should Migrations create columns as null by default? (default: `false`)
-- `add_timestamps_use_datetime`: Should Migrations use `DATETIME` type
- columns for the columns added by `addTimestamps()`.
-
-Set them via Configure to enable (e.g. in `config/app.php`):
-
-```text
-'Migrations' => [
- 'unsigned_primary_keys' => true,
- 'unsigned_ints' => true,
- 'column_null_default' => true,
-],
-```
-
-> [!NOTE]
-> The `unsigned_primary_keys` and `unsigned_ints` options only affect MySQL databases.
-> When generating migrations with `bake migration_snapshot` or `bake migration_diff`,
-> the `signed` attribute will only be included in the output for unsigned columns
-> (as `'signed' => false`). Signed is the default for integer columns in MySQL, so
-> `'signed' => true` is never output.
+Compatibility feature flags are covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Skipping the `schema.lock` file generation
-In order for the diff feature to work, a **.lock** file is generated everytime
-you migrate, rollback or bake a snapshot, to keep track of the state of your
-database schema at any given point in time. You can skip this file generation,
-for instance when deploying on your production environment, by using the
-`--no-lock` option for the aforementioned command:
-
-```bash
-bin/cake migrations migrate --no-lock
-
-bin/cake migrations rollback --no-lock
-
-bin/cake bake migration_snapshot MyMigration --no-lock
-```
+The `--no-lock` workflow is covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Deployment
-You should update your deployment scripts to run migrations when new code is
-deployed. Ideally you want to run migrations after the code is on your servers,
-but before the application code becomes active.
-
-After running migrations remember to clear the ORM cache so it renews the column
-metadata of your tables. Otherwise, you might end up having errors about
-columns not existing when performing operations on those new columns. The
-CakePHP Core includes a [Schema Cache Shell](https://book.cakephp.org/5/en/console-and-shells/schema-cache.html) that you
-can use to perform this operation:
-
-```bash
-bin/cake migration migrate
-bin/cake schema_cache clear
-```
+Deployment guidance and schema cache refresh steps are covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## Alert of missing migrations
-You can use the `Migrations.PendingMigrations` middleware in local development
-to alert developers about new migrations that have not been applied:
-
-```php
-use Migrations\Middleware\PendingMigrationsMiddleware;
-
-$config = [
- 'plugins' => [
- ... // Optionally include a list of plugins with migrations to check.
- ],
-];
-
-$middlewareQueue
- ... // ErrorHandler middleware
- ->add(new PendingMigrationsMiddleware($config))
- ... // rest
-```
-
-You can add `'app'` config key set to `false` if you are only interested in
-checking plugin migrations.
-
-You can temporarily disable the migration check by adding
-`skip-migration-check=1` to the URL query string
+Local development alerts for pending migrations are covered in
+[Integration and Deployment](advanced/integration-and-deployment).
## IDE autocomplete support
-The [IdeHelper plugin](https://github.com/dereuromark/cakephp-ide-helper) can help
-you to get more IDE support for the tables, their column names and possible column types.
-Specifically PHPStorm understands the meta information and can help you autocomplete those.
+IDE-related tooling notes are covered in
+[Integration and Deployment](advanced/integration-and-deployment).
diff --git a/docs/en/index.rst b/docs/en/index.rst
deleted file mode 100644
index f180413e9..000000000
--- a/docs/en/index.rst
+++ /dev/null
@@ -1,955 +0,0 @@
-Migrations 5.x
-##############
-
-Migrations is a plugin that lets you track changes to your database schema over
-time as PHP code that accompanies your application. This lets you ensure each
-environment your application runs in has the appropriate schema by applying
-migrations.
-
-Instead of writing schema modifications in SQL, this plugin allows you to
-define schema changes with a high-level database portable API.
-
-What's New in 5.x
-=================
-
-Migrations 5.x includes several new features:
-
-- **Seed tracking** - Seeds are now tracked in a ``cake_seeds`` table, preventing
- accidental re-runs
-- **Check constraints** - Support for database check constraints via ``addCheckConstraint()``
-- **Default values in bake** - Specify default values when baking migrations
- (e.g., ``active:boolean:default[true]``)
-- **MySQL ALTER options** - Control ``ALGORITHM`` and ``LOCK`` for ALTER TABLE operations
-- **insertOrSkip()** - New method for idempotent seed data insertion
-
-See the :doc:`upgrading` guide for breaking changes and migration steps from 4.x.
-
-Installation
-============
-
-By default Migrations is installed with the application skeleton. If
-you've removed it and want to re-install it, you can do so by running the
-following from your application's ROOT directory (where **composer.json** file is
-located):
-
-.. code-block:: bash
-
- php composer.phar require cakephp/migrations "@stable"
-
- # Or if composer is installed globally
- composer require cakephp/migrations "@stable"
-
-To use the plugin you'll need to load it in your application's
-**config/bootstrap.php** file. You can use `CakePHP's Plugin shell
-`__ to
-load and unload plugins from your **config/bootstrap.php**:
-
-.. code-block:: bash
-
- bin/cake plugin load Migrations
-
-Or you can load the plugin by editing your **src/Application.php** file and
-adding the following statement::
-
- $this->addPlugin('Migrations');
-
-Additionally, you will need to configure the default database configuration for
-your application in your **config/app.php** file as explained in the `Database
-Configuration section
-`__.
-
-Upgrading from 4.x
-==================
-
-If you are upgrading from Migrations 4.x, please see the :doc:`upgrading` guide
-for breaking changes and migration steps.
-
-Overview
-========
-
-A migration is a PHP file that describes the changes to apply to your database.
-A migration file can add, change or remove tables, columns, indexes and foreign keys.
-
-If we wanted to create a table, we could use a migration similar to this::
-
- table('products');
- $table->addColumn('name', 'string', [
- 'default' => null,
- 'limit' => 255,
- 'null' => false,
- ]);
- $table->addColumn('description', 'text', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('created', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('modified', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->create();
- }
- }
-
-When applied, this migration will add a table to your database named
-``products`` with the following column definitions:
-
-- ``id`` column of type ``integer`` as primary key. This column is added
- implicitly, but you can customize the name and type if necessary.
-- ``name`` column of type ``string``
-- ``description`` column of type ``text``
-- ``created`` column of type ``datetime``
-- ``modified`` column of type ``datetime``
-
-.. note::
-
- Migrations are not automatically applied, you can apply and rollback
- migrations with CLI commands.
-
-Once the file has been created in the **config/Migrations** folder, you can
-apply it:
-
-.. code-block:: bash
-
- bin/cake migrations migrate
-
-Creating Migrations
-===================
-
-Migration files are stored in the **config/Migrations** directory of your
-application. The name of the migration files are prefixed with the date in
-which they were created, in the format **YYYYMMDDHHMMSS_MigrationName.php**.
-Here are examples of migration filenames:
-
-* **20160121163850_CreateProducts.php**
-* **20160210133047_AddRatingToProducts.php**
-
-The easiest way to create a migrations file is by using ``bin/cake bake
-migration`` CLI command:
-
-.. code-block:: bash
-
- bin/cake bake migration CreateProducts
-
-This will create an empty migration that you can edit to add any columns,
-indexes and foreign keys you need. See the :ref:`creating-a-table` section to
-learn more about using migrations to define tables.
-
-.. note::
-
- Migrations need to be applied using ``bin/cake migrations migrate`` after
- they have been created.
-
-Migration file names
---------------------
-
-When generating a migration, you can follow one of the following patterns
-to have additional skeleton code generated:
-
-* ``/^(Create)(.*)/`` Creates the specified table.
-* ``/^(Drop)(.*)/`` Drops the specified table.
- Ignores specified field arguments
-* ``/^(Add).*(?:To)(.*)/`` Adds fields to the specified
- table
-* ``/^(Remove).*(?:From)(.*)/`` Removes fields from the
- specified table
-* ``/^(Alter)(.*)/`` Alters the specified table. An alias
- for CreateTable and AddField.
-* ``/^(Alter).*(?:On)(.*)/`` Alters fields from the specified table.
-
-You can also use the ``underscore_form`` as the name for your migrations i.e.
-``create_products``.
-
-.. warning::
-
- Migration names are used as class names, and thus may collide with
- other migrations if the class names are not unique. In this case, it may be
- necessary to manually override the name at a later date, or simply change
- the name you are specifying.
-
-Creating a table
-----------------
-
-You can use ``bake migration`` to create a table:
-
-.. code-block:: bash
-
- bin/cake bake migration CreateProducts name:string description:text created modified
-
-The command line above will generate a migration file that resembles::
-
- table('products');
- $table->addColumn('name', 'string', [
- 'default' => null,
- 'limit' => 255,
- 'null' => false,
- ]);
- $table->addColumn('description', 'text', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('created', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->addColumn('modified', 'datetime', [
- 'default' => null,
- 'null' => false,
- ]);
- $table->create();
- }
- }
-
-Column syntax
--------------
-
-The ``bake migration`` command provides a compact syntax to define columns when
-generating a migration:
-
-.. code-block:: bash
-
- bin/cake bake migration CreateProducts name:string description:text created modified
-
-You can use the column syntax when creating tables and adding columns. You can
-also edit the migration after generation to add or customize the columns
-
-Columns on the command line follow the following pattern::
-
- fieldName:fieldType?[length]:default[value]:indexType:indexName
-
-For instance, the following are all valid ways of specifying an email field:
-
-* ``email:string?``
-* ``email:string:unique``
-* ``email:string?[50]``
-* ``email:string:unique:EMAIL_INDEX``
-* ``email:string[120]:unique:EMAIL_INDEX``
-
-While defining decimal columns, the ``length`` can be defined to have precision
-and scale, separated by a comma.
-
-* ``amount:decimal[5,2]``
-* ``amount:decimal?[5,2]``
-
-Columns with a question mark after the fieldType will make the column nullable.
-
-The ``length`` part is optional and should always be written between bracket.
-
-The ``default[value]`` part is optional and sets the default value for the column.
-Supported value types include:
-
-* Booleans: ``true`` or ``false`` - e.g., ``active:boolean:default[true]``
-* Integers: ``0``, ``123``, ``-456`` - e.g., ``count:integer:default[0]``
-* Floats: ``1.5``, ``-2.75`` - e.g., ``rate:decimal:default[1.5]``
-* Strings: ``'hello'`` or ``"world"`` (quoted) - e.g., ``status:string:default['pending']``
-* Null: ``null`` or ``NULL`` - e.g., ``description:text?:default[null]``
-* SQL expressions: ``CURRENT_TIMESTAMP`` - e.g., ``created_at:datetime:default[CURRENT_TIMESTAMP]``
-
-Fields named ``created`` and ``modified``, as well as any field with a ``_at``
-suffix, will automatically be set to the type ``datetime``.
-
-There are some heuristics to choosing fieldtypes when left unspecified or set to
-an invalid value. Default field type is ``string``:
-
-* id: integer
-* created, modified, updated: datetime
-* latitude, longitude (or short forms lat, lng): decimal
-
-Additionally you can create an empty migrations file if you want full control
-over what needs to be executed, by omitting to specify a columns definition:
-
-.. code-block:: bash
-
- bin/cake migrations create MyCustomMigration
-
-
-See :doc:`writing-migrations` for more information on how to use ``Table``
-objects to interact with tables and define schema changes.
-
-Adding columns to an existing table
------------------------------------
-
-If the migration name in the command line is of the form "AddXXXToYYY" and is
-followed by a list of column names and types then a migration file containing
-the code for creating the columns will be generated:
-
-.. code-block:: bash
-
- bin/cake bake migration AddPriceToProducts price:decimal[5,2]
-
-Executing the command line above will generate::
-
- table('products');
- $table->addColumn('price', 'decimal', [
- 'default' => null,
- 'null' => false,
- 'precision' => 5,
- 'scale' => 2,
- ]);
- $table->update();
- }
- }
-
-Adding a column with an index
------------------------------
-
-It is also possible to add indexes to columns:
-
-.. code-block:: bash
-
- bin/cake bake migration AddNameIndexToProducts name:string:index
-
-will generate::
-
- table('products');
- $table->addColumn('name', 'string')
- ->addColumn('email', 'string')
- ->addIndex(['name'])
- // add a unique index:
- ->addIndex('email', ['unique' => true])
- ->update();
- }
- }
-
-Adding a column with a default value
--------------------------------------
-
-You can specify default values for columns using the ``default[value]`` syntax:
-
-.. code-block:: bash
-
- bin/cake bake migration AddActiveToUsers active:boolean:default[true]
-
-will generate::
-
- table('users');
- $table->addColumn('active', 'boolean', [
- 'default' => true,
- 'null' => false,
- ]);
- $table->update();
- }
- }
-
-You can combine default values with other options like nullable and indexes:
-
-.. code-block:: bash
-
- bin/cake bake migration AddStatusToOrders status:string:default['pending']:unique
-
-Altering a column
------------------
-
-In the same way, you can generate a migration to alter a column by using the
-command line, if the migration name is of the form "AlterXXXOnYYY":
-
-.. code-block:: bash
-
- bin/cake bake migration AlterPriceOnProducts name:float
-
-will generate::
-
- table('products');
- $table->changeColumn('name', 'float');
- $table->update();
- }
- }
-
-.. warning::
-
- Changing the type of a column can result in data loss if the
- current and target column type are not compatible. For example converting
- a varchar to float.
-
-Removing a column
------------------
-
-In the same way, you can generate a migration to remove a column by using the
-command line, if the migration name is of the form "RemoveXXXFromYYY":
-
-.. code-block:: bash
-
- bin/cake bake migration RemovePriceFromProducts price
-
-creates the file::
-
- table('products');
- $table->removeColumn('price')
- ->save();
- }
- }
-
-.. note::
-
- The `removeColumn` command is not reversible, so must be called in the
- `up` method. A corresponding `addColumn` call should be added to the
- `down` method.
-
-Generating migration snapshots from an existing database
-========================================================
-
-If you have a pre-existing database and want to start using
-migrations, or to version control the initial schema of your application's
-database, you can run the ``bake migration_snapshot`` command:
-
-.. code-block:: bash
-
- bin/cake bake migration_snapshot Initial
-
-It will generate a migration file called **YYYYMMDDHHMMSS_Initial.php**
-containing all the create statements for all tables in your database.
-
-By default, the snapshot will be created by connecting to the database defined
-in the ``default`` connection configuration. If you need to bake a snapshot from
-a different datasource, you can use the ``--connection`` option:
-
-.. code-block:: bash
-
- bin/cake bake migration_snapshot Initial --connection my_other_connection
-
-You can also make sure the snapshot includes only the tables for which you have
-defined the corresponding model classes by using the ``--require-table`` flag:
-
-.. code-block:: bash
-
- bin/cake bake migration_snapshot Initial --require-table
-
-When using the ``--require-table`` flag, the shell will look through your
-application ``Table`` classes and will only add the model tables in the snapshot.
-
-If you want to generate a snapshot without marking it as migrated (for example,
-for use in unit tests), you can use the ``--generate-only`` flag:
-
-.. code-block:: bash
-
- bin/cake bake migration_snapshot Initial --generate-only
-
-This will create the migration file but will not add an entry to the migrations
-tracking table, allowing you to move the file to a different location without causing
-"MISSING" status issues.
-
-The same logic will be applied implicitly if you wish to bake a snapshot for a
-plugin. To do so, you need to use the ``--plugin`` option:
-
-.. code-block:: bash
-
- bin/cake bake migration_snapshot Initial --plugin MyPlugin
-
-Only the tables which have a ``Table`` object model class defined will be added
-to the snapshot of your plugin.
-
-.. note::
-
- When baking a snapshot for a plugin, the migration files will be created
- in your plugin's **config/Migrations** directory.
-
-Be aware that when you bake a snapshot, it is automatically added to the
-migrations log table as migrated.
-
-Generating a diff
-=================
-
-As migrations are applied and rolled back, the migrations plugin will generate
-a 'dump' file of your schema. If you make manual changes to your database schema
-outside of migrations, you can use ``bake migration_diff`` to generate
-a migration file that captures the difference between the current schema dump
-file and database schema. To do so, you can use the following command:
-
-.. code-block:: bash
-
- bin/cake bake migration_diff NameOfTheMigrations
-
-By default, the diff will be created by connecting to the database defined
-in the ``default`` connection configuration.
-If you need to bake a diff from a different datasource, you can use the
-``--connection`` option:
-
-.. code-block:: bash
-
- bin/cake bake migration_diff NameOfTheMigrations --connection my_other_connection
-
-If you want to use the diff feature on an application that already has a
-migrations history, you need to manually create the dump file that will be used
-as comparison:
-
-.. code-block:: bash
-
- bin/cake migrations dump
-
-The database state must be the same as it would be if you just migrated all
-your migrations before you create a dump file.
-Once the dump file is generated, you can start doing changes in your database
-and use the ``bake migration_diff`` command whenever you see fit.
-
-.. note::
-
- Migration diff generation can not detect column renamings.
-
-Applying Migrations
-===================
-
-Once you have generated or written your migration file, you need to execute the
-following command to apply the changes to your database:
-
-.. code-block:: bash
-
- # Run all the migrations
- bin/cake migrations migrate
-
- # Migrate to a specific version using the ``--target`` option
- # or ``-t`` for short.
- # The value is the timestamp that is prefixed to the migrations file name::
- bin/cake migrations migrate -t 20150103081132
-
- # By default, migration files are looked for in the **config/Migrations**
- # directory. You can specify the directory using the ``--source`` option
- # or ``-s`` for short.
- # The following example will run migrations in the **config/Alternate**
- # directory
- bin/cake migrations migrate -s Alternate
-
- # You can run migrations to a different connection than the ``default`` one
- # using the ``--connection`` option or ``-c`` for short
- bin/cake migrations migrate -c my_custom_connection
-
- # Migrations can also be run for plugins. Simply use the ``--plugin`` option
- # or ``-p`` for short
- bin/cake migrations migrate -p MyAwesomePlugin
-
-Reverting Migrations
-====================
-
-The rollback command is used to undo previous migrations executed by this
-plugin. It is the reverse action of the ``migrate`` command:
-
-.. code-block:: bash
-
- # You can rollback to the previous migration by using the
- # ``rollback`` command::
- bin/cake migrations rollback
-
- # You can also pass a migration version number to rollback
- # to a specific version::
- bin/cake migrations rollback -t 20150103081132
-
-You can also use the ``--source``, ``--connection`` and ``--plugin`` options
-just like for the ``migrate`` command.
-
-View Migrations Status
-======================
-
-The Status command prints a list of all migrations, along with their current
-status. You can use this command to determine which migrations have been run:
-
-.. code-block:: bash
-
- bin/cake migrations status
-
-You can also output the results as a JSON formatted string using the
-``--format`` option (or ``-f`` for short):
-
-.. code-block:: bash
-
- bin/cake migrations status --format json
-
-You can also use the ``--source``, ``--connection`` and ``--plugin`` options
-just like for the ``migrate`` command.
-
-Cleaning up missing migrations
--------------------------------
-
-Sometimes migration files may be deleted from the filesystem but still exist
-in the migrations tracking table. These migrations will be marked as **MISSING** in the
-status output. You can remove these entries from the tracking table using the
-``--cleanup`` option:
-
-.. code-block:: bash
-
- bin/cake migrations status --cleanup
-
-This will remove all migration entries from the tracking table that no longer
-have corresponding migration files in the filesystem.
-
-Marking a migration as migrated
-===============================
-
-It can sometimes be useful to mark a set of migrations as migrated without
-actually running them. In order to do this, you can use the ``mark_migrated``
-command. The command works seamlessly as the other commands.
-
-You can mark all migrations as migrated using this command:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated
-
-You can also mark all migrations up to a specific version as migrated using
-the ``--target`` option:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated --target=20151016204000
-
-If you do not want the targeted migration to be marked as migrated during the
-process, you can use the ``--exclude`` flag with it:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated --target=20151016204000 --exclude
-
-Finally, if you wish to mark only the targeted migration as migrated, you can
-use the ``--only`` flag:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated --target=20151016204000 --only
-
-You can also use the ``--source``, ``--connection`` and ``--plugin`` options
-just like for the ``migrate`` command.
-
-.. note::
-
- When you bake a snapshot with the ``cake bake migration_snapshot``
- command, the created migration will automatically be marked as migrated.
- To prevent this behavior (e.g., for unit test migrations), use the
- ``--generate-only`` flag.
-
-This command expects the migration version number as argument:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated 20150420082532
-
-If you wish to mark all migrations as migrated, you can use the ``all`` special
-value. If you use it, it will mark all found migrations as migrated:
-
-.. code-block:: bash
-
- bin/cake migrations mark_migrated all
-
-Seeding your database
-=====================
-
-Seed classes are a good way to populate your database with default or starter
-data. They are also a great way to generate data for development environments.
-
-By default, seeds will be looked for in the ``config/Seeds/`` directory of
-your application. See the :doc:`seeding` for how to build and use seed classes.
-
-Generating a dump file
-======================
-
-The dump command creates a file to be used with the ``bake migration_diff``
-command:
-
-.. code-block:: bash
-
- bin/cake migrations dump
-
-Each generated dump file is specific to the Connection it is generated from (and
-is suffixed as such). This allows the ``bake migration_diff`` command to
-properly compute diff in case your application is dealing with multiple database
-possibly from different database vendors.
-
-Dump files are created in the same directory as your migrations files.
-
-You can also use the ``--source``, ``--connection`` and ``--plugin`` options
-just like for the ``migrate`` command.
-
-
-Using Migrations for Tests
-==========================
-
-If you are using migrations for your application schema you can also use those
-same migrations to build schema in your tests. In your application's
-``tests/bootstrap.php`` file you can use the ``Migrator`` class to build schema
-when tests are run. The ``Migrator`` will use existing schema if it is current,
-and if the migration history that is in the database differs from what is in the
-filesystem, all tables will be dropped and migrations will be rerun from the
-beginning::
-
- // in tests/bootstrap.php
- use Migrations\TestSuite\Migrator;
-
- $migrator = new Migrator();
-
- // Simple setup for with no plugins
- $migrator->run();
-
- // Run a non 'test' database
- $migrator->run(['connection' => 'test_other']);
-
- // Run migrations for plugins
- $migrator->run(['plugin' => 'Contacts']);
-
- // Run the Documents migrations on the test_docs connection.
- $migrator->run(['plugin' => 'Documents', 'connection' => 'test_docs']);
-
-
-If you need to run multiple sets of migrations, those can be run as follows::
-
- // Run migrations for plugin Contacts on the ``test`` connection, and Documents on the ``test_docs`` connection
- $migrator->runMany([
- ['plugin' => 'Contacts'],
- ['plugin' => 'Documents', 'connection' => 'test_docs']
- ]);
-
-If your database also contains tables that are not managed by your application
-like those created by PostGIS, then you can exclude those tables from the drop
-& truncate behavior using the ``skip`` option::
-
- $migrator->run(['connection' => 'test', 'skip' => ['postgis*']]);
-
-The ``skip`` option accepts a ``fnmatch()`` compatible pattern to exclude tables
-from drop & truncate operations.
-
-If you need to see additional debugging output from migrations are being run,
-you can enable a ``debug`` level logger.
-
-Using Migrations In Plugins
-===========================
-
-Plugins can also provide migration files. This makes plugins that are intended
-to be distributed much more portable and easy to install. All commands in the
-Migrations plugin support the ``--plugin`` or ``-p`` option that will scope the
-execution to the migrations relative to that plugin:
-
-.. code-block:: bash
-
- bin/cake migrations status -p PluginName
-
- bin/cake migrations migrate -p PluginName
-
-Running Migrations in a non-shell environment
-=============================================
-
-While typical usage of migrations is from the command line, you can also run
-migrations from a non-shell environment, by using
-``Migrations\Migrations`` class. This can be handy in case you are developing a plugin
-installer for a CMS for instance. The ``Migrations`` class allows you to run the
-following commands from the migrations shell:
-
-* migrate
-* rollback
-* markMigrated
-* status
-* seed
-
-Each of these commands has a method defined in the ``Migrations`` class.
-
-Here is how to use it::
-
- use Migrations\Migrations;
-
- $migrations = new Migrations();
-
- // Will return an array of all migrations and their status
- $status = $migrations->status();
-
- // Will return true if success. If an error occurred, an exception will be thrown
- $migrate = $migrations->migrate();
-
- // Will return true if success. If an error occurred, an exception will be thrown
- $rollback = $migrations->rollback();
-
- // Will return true if success. If an error occurred, an exception will be thrown
- $markMigrated = $migrations->markMigrated(20150804222900);
-
- // Will return true if success. If an error occurred, an exception will be thrown
- $seeded = $migrations->seed();
-
-The methods can accept an array of parameters that should match options from
-the commands::
-
- use Migrations\Migrations;
-
- $migrations = new Migrations();
-
- // Will return an array of all migrations and their status
- $status = $migrations->status(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-
-You can pass any options the shell commands would take.
-The only exception is the ``markMigrated`` command which is expecting the
-version number of the migrations to mark as migrated as first argument. Pass
-the array of parameters as the second argument for this method.
-
-Optionally, you can pass these parameters in the constructor of the class.
-They will be used as default and this will prevent you from having to pass
-them on each method call::
-
- use Migrations\Migrations;
-
- $migrations = new Migrations(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-
- // All the following calls will be done with the parameters passed to the Migrations class constructor
- $status = $migrations->status();
- $migrate = $migrations->migrate();
-
-If you need to override one or more default parameters for one call, you can
-pass them to the method::
-
- use Migrations\Migrations;
-
- $migrations = new Migrations(['connection' => 'custom', 'source' => 'MyMigrationsFolder']);
-
- // This call will be made with the "custom" connection
- $status = $migrations->status();
- // This one with the "default" connection
- $migrate = $migrations->migrate(['connection' => 'default']);
-
-.. _feature-flags:
-
-Feature Flags
-=============
-
-Migrations offers a few feature flags for compatibility. These features are disabled by default but can be enabled if required:
-
-* ``unsigned_primary_keys``: Should Migrations create primary keys as unsigned integers? (default: ``false``)
-* ``unsigned_ints``: Should Migrations create all integer columns as unsigned? (default: ``false``)
-* ``column_null_default``: Should Migrations create columns as null by default? (default: ``false``)
-* ``add_timestamps_use_datetime``: Should Migrations use ``DATETIME`` type
- columns for the columns added by ``addTimestamps()``.
-
-Set them via Configure to enable (e.g. in ``config/app.php``)::
-
- 'Migrations' => [
- 'unsigned_primary_keys' => true,
- 'unsigned_ints' => true,
- 'column_null_default' => true,
- ],
-
-.. note::
-
- The ``unsigned_primary_keys`` and ``unsigned_ints`` options only affect MySQL databases.
- When generating migrations with ``bake migration_snapshot`` or ``bake migration_diff``,
- the ``signed`` attribute will only be included in the output for unsigned columns
- (as ``'signed' => false``). Signed is the default for integer columns in MySQL, so
- ``'signed' => true`` is never output.
-
-Skipping the ``schema.lock`` file generation
-============================================
-
-In order for the diff feature to work, a **.lock** file is generated everytime
-you migrate, rollback or bake a snapshot, to keep track of the state of your
-database schema at any given point in time. You can skip this file generation,
-for instance when deploying on your production environment, by using the
-``--no-lock`` option for the aforementioned command:
-
-.. code-block:: bash
-
- bin/cake migrations migrate --no-lock
-
- bin/cake migrations rollback --no-lock
-
- bin/cake bake migration_snapshot MyMigration --no-lock
-
-Deployment
-==========
-
-You should update your deployment scripts to run migrations when new code is
-deployed. Ideally you want to run migrations after the code is on your servers,
-but before the application code becomes active.
-
-After running migrations remember to clear the ORM cache so it renews the column
-metadata of your tables. Otherwise, you might end up having errors about
-columns not existing when performing operations on those new columns. The
-CakePHP Core includes a `Schema Cache Shell
-`__ that you
-can use to perform this operation:
-
-.. code-block:: bash
-
- bin/cake migration migrate
- bin/cake schema_cache clear
-
-Alert of missing migrations
-===========================
-
-You can use the ``Migrations.PendingMigrations`` middleware in local development
-to alert developers about new migrations that have not been applied::
-
- use Migrations\Middleware\PendingMigrationsMiddleware;
-
- $config = [
- 'plugins' => [
- ... // Optionally include a list of plugins with migrations to check.
- ],
- ];
-
- $middlewareQueue
- ... // ErrorHandler middleware
- ->add(new PendingMigrationsMiddleware($config))
- ... // rest
-
-You can add ``'app'`` config key set to ``false`` if you are only interested in
-checking plugin migrations.
-
-You can temporarily disable the migration check by adding
-``skip-migration-check=1`` to the URL query string
-
-IDE autocomplete support
-========================
-
-The `IdeHelper plugin
-`__ can help
-you to get more IDE support for the tables, their column names and possible column types.
-Specifically PHPStorm understands the meta information and can help you autocomplete those.
diff --git a/docs/en/seeding.rst b/docs/en/seeding.rst
deleted file mode 100644
index d2aedf34b..000000000
--- a/docs/en/seeding.rst
+++ /dev/null
@@ -1,616 +0,0 @@
-Database Seeding
-################
-
-Seed classes are a great way to easily fill your database with data after
-it's created. By default, they are stored in the ``config/Seeds`` directory.
-
-.. note::
-
- Database seeding is entirely optional, and Migrations does not create a Seeds
- directory by default.
-
-Creating a New Seed Class
-=========================
-
-Migrations includes a command to easily generate a new seed class:
-
-.. code-block:: bash
-
- $ bin/cake bake seed MyNewSeed
-
-By default, it generates a traditional seed class with a named class:
-
-.. code-block:: php
-
- call('Articles');`` or ``$this->call('ArticlesSeed');``
-
-Using the short name is recommended for cleaner, more concise code.
-
-Anonymous Seed Classes
-----------------------
-
-Migrations also supports generating anonymous seed classes, which use PHP's
-anonymous class feature instead of named classes. This style is useful for:
-
-- Avoiding namespace declarations
-- Better PHPCS compatibility (no class name to filename matching required)
-- Simpler file structure without named class constraints
-
-To generate an anonymous seed class, use the ``--style anonymous`` option:
-
-.. code-block:: bash
-
- $ bin/cake bake seed MyNewSeed --style anonymous
-
-This generates a seed file using an anonymous class:
-
-.. code-block:: php
-
- [
- 'style' => 'anonymous', // or 'traditional'
- ],
-
-Seed Options
-------------
-
-.. code-block:: bash
- # You specify the name of the table the seed files will alter by using the ``--table`` option
- bin/cake bake seed Articles --table my_articles_table
-
- # You can specify a plugin to bake into
- bin/cake bake seed Articles --plugin PluginName
-
- # You can specify an alternative connection when generating a seed.
- bin/cake bake seed Articles --connection connection
-
- # Include data from the Articles table in your seed.
- bin/cake bake seed --data Articles
-
-By default, it will export all the rows found in your table. You can limit the
-number of rows exported by using the ``--limit`` option:
-
-.. code-block:: bash
-
- # Will only export the first 10 rows found
- bin/cake bake seed --data --limit 10 Articles
-
-If you only want to include a selection of fields from the table in your seed
-file, you can use the ``--fields`` option. It takes the list of fields to
-include as a comma separated value string:
-
-.. code-block:: bash
-
- # Will only export the fields `id`, `title` and `excerpt`
- bin/cake bake seed --data --fields id,title,excerpt Articles
-
-.. tip::
-
- Of course you can use both the ``--limit`` and ``--fields`` options in the
- same command call.
-
-.. _custom-seed-migration-templates:
-
-Customizing Seed and Migration templates
-----------------------------------------
-
-Because migrations uses `bake `__ under the hood
-you can customize the templates that migrations uses for creating seeds and
-migrations by creating templates in your application. Custom templates for
-migrations should be on one of the following paths:
-
-- ``ROOT/templates/plugin/Migrations/bake/``
-- ``ROOT/templates/bake/``
-
-For example, the seed templates are:
-
-- Traditional: ``Seed/seed.twig`` at **ROOT/templates/plugin/Migrations/bake/Seed/seed.twig**
-- Anonymous: ``Seed/seed-anonymous.twig`` at **ROOT/templates/plugin/Migrations/bake/Seed/seed-anonymous.twig**
-
-The BaseSeed Class
-==================
-
-All Migrations seeds extend from the ``BaseSeed`` class.
-It provides the necessary support to create your seed classes. Seed
-classes are primarily used to insert test data.
-
-The Run Method
-==============
-
-The run method is automatically invoked by Migrations when you execute the
-``cake seeds run`` command. You should use this method to insert your test
-data.
-
-Seed Execution Tracking
-========================
-
-Seeds track their execution state in the ``cake_seeds`` database table. By default,
-a seed will only run once. If you attempt to run a seed that has already been
-executed, it will be skipped with an "already executed" message.
-
-To re-run a seed that has already been executed, use the ``--force`` flag:
-
-.. code-block:: bash
-
- bin/cake seeds run Users --force
-
-You can check which seeds have been executed using the status command:
-
-.. code-block:: bash
-
- bin/cake seeds status
-
-To reset all seeds' execution state (allowing them to run again without ``--force``):
-
-.. code-block:: bash
-
- bin/cake seeds reset
-
-.. note::
-
- When re-running seeds with ``--force``, be careful to ensure your seeds are
- idempotent (safe to run multiple times) or they may create duplicate data.
-
-Customizing the Seed Tracking Table
-------------------------------------
-
-By default, seed execution is tracked in a table named ``cake_seeds``. You can
-customize this table name by configuring it in your ``config/app.php`` or
-``config/app_local.php``:
-
-.. code-block:: php
-
- 'Migrations' => [
- 'seed_table' => 'my_custom_seeds_table',
- ],
-
-This is useful if you need to avoid table name conflicts or want to follow
-a specific naming convention in your database.
-
-Idempotent Seeds
-================
-
-Some seeds are designed to be run multiple times safely (idempotent), such as seeds
-that update configuration or reference data. For these seeds, you can override the
-``isIdempotent()`` method:
-
-.. code-block:: php
-
- execute("
- INSERT INTO settings (setting_key, setting_value)
- VALUES ('app_version', '2.0.0')
- ON DUPLICATE KEY UPDATE setting_value = '2.0.0'
- ");
-
- // Or check before inserting
- $exists = $this->fetchRow(
- "SELECT COUNT(*) as count FROM settings WHERE setting_key = 'maintenance_mode'"
- );
-
- if ($exists['count'] === 0) {
- $this->table('settings')->insert([
- 'setting_key' => 'maintenance_mode',
- 'setting_value' => 'false',
- ])->save();
- }
- }
- }
-
-When ``isIdempotent()`` returns ``true``:
-
-- The seed will run **every time** you execute ``seeds run``
-- The last execution time is still tracked in the ``cake_seeds`` table
-- The ``seeds status`` command will show the seed as ``(idempotent)``
-- You must ensure the seed's ``run()`` method handles duplicate executions safely
-
-This is useful for:
-
-- Configuration seeds that should always reflect current values
-- Reference data that may need periodic updates
-- Seeds that use ``INSERT ... ON DUPLICATE KEY UPDATE`` or similar patterns
-- Development/testing seeds that need to run repeatedly
-
-.. warning::
-
- Only mark a seed as idempotent if you've verified it's safe to run multiple times.
- Otherwise, you may create duplicate data or other unexpected behavior.
-
-The Init Method
-===============
-
-The ``init()`` method is run by Migrations before the run method if it exists. This
-can be used to initialize properties of the Seed class before using run.
-
-The Should Execute Method
-=========================
-
-The ``shouldExecute()`` method is run by Migrations before executing the seed.
-This can be used to prevent the seed from being executed at this time. It always
-returns true by default. You can override it in your custom ``BaseSeed``
-implementation.
-
-Foreign Key Dependencies
-========================
-
-Often you'll find that seeds need to run in a particular order, so they don't
-violate foreign key constraints. To define this order, you can implement the
-``getDependencies()`` method that returns an array of seeds to run before the
-current seed:
-
-.. code-block:: php
-
- call('Another'); // Short name without 'Seed' suffix
- $this->call('YetAnother'); // Short name without 'Seed' suffix
-
- // You can use the plugin dot syntax to call seeds from a plugin
- $this->call('PluginName.FromPlugin');
- }
- }
-
-You can also use the full seed name including the ``Seed`` suffix:
-
-.. code-block:: php
-
- $this->call('AnotherSeed');
- $this->call('YetAnotherSeed');
- $this->call('PluginName.FromPluginSeed');
-
-Both forms are supported and work identically.
-
-Inserting Data
-==============
-
-Seed classes can also use the familiar ``Table`` object to insert data. You can
-retrieve an instance of the Table object by calling the ``table()`` method from
-within your seed class and then use the ``insert()`` method to insert data:
-
-.. code-block:: php
-
- 'foo',
- 'created' => date('Y-m-d H:i:s'),
- ],[
- 'body' => 'bar',
- 'created' => date('Y-m-d H:i:s'),
- ]
- ];
-
- $posts = $this->table('posts');
- $posts->insert($data)
- ->saveData();
- }
- }
-
-.. note::
-
- You must call the ``saveData()`` method to commit your data to the table.
- Migrations will buffer data until you do so.
-
-Insert Modes
-============
-
-In addition to the standard ``insert()`` method, Migrations provides specialized
-insert methods for handling conflicts with existing data.
-
-Insert or Skip
---------------
-
-The ``insertOrSkip()`` method inserts rows but silently skips any that would
-violate a unique constraint:
-
-.. code-block:: php
-
- 'USD', 'name' => 'US Dollar'],
- ['code' => 'EUR', 'name' => 'Euro'],
- ];
-
- $this->table('currencies')
- ->insertOrSkip($data)
- ->saveData();
- }
- }
-
-Insert or Update (Upsert)
--------------------------
-
-The ``insertOrUpdate()`` method performs an "upsert" operation - inserting new
-rows and updating existing rows that conflict on unique columns:
-
-.. code-block:: php
-
- 'USD', 'rate' => 1.0000],
- ['code' => 'EUR', 'rate' => 0.9234],
- ];
-
- $this->table('exchange_rates')
- ->insertOrUpdate($data, ['rate'], ['code'])
- ->saveData();
- }
- }
-
-The method takes three arguments:
-
-- ``$data``: The rows to insert (same format as ``insert()``)
-- ``$updateColumns``: Which columns to update when a conflict occurs
-- ``$conflictColumns``: Which columns define uniqueness (must have a unique index)
-
-.. warning::
-
- Database-specific behavior differences:
-
- **MySQL**: Uses ``ON DUPLICATE KEY UPDATE``. The ``$conflictColumns`` parameter
- is ignored because MySQL automatically applies the update to *all* unique
- constraint violations on the table. Passing ``$conflictColumns`` will trigger
- a warning. If your table has multiple unique constraints, be aware that a
- conflict on *any* of them will trigger the update.
-
- **PostgreSQL/SQLite**: Uses ``ON CONFLICT (...) DO UPDATE SET``. The
- ``$conflictColumns`` parameter is required and specifies exactly which unique
- constraint should trigger the update. A ``RuntimeException`` will be thrown
- if this parameter is empty.
-
- **SQL Server**: Not currently supported. Use separate insert/update logic.
-
-Truncating Tables
-=================
-
-In addition to inserting data Migrations makes it trivial to empty your tables using the
-SQL `TRUNCATE` command:
-
-.. code-block:: php
-
- 'foo',
- 'created' => date('Y-m-d H:i:s'),
- ],
- [
- 'body' => 'bar',
- 'created' => date('Y-m-d H:i:s'),
- ]
- ];
-
- $posts = $this->table('posts');
- $posts->insert($data)
- ->saveData();
-
- // empty the table
- $posts->truncate();
- }
- }
-
-.. note::
-
- SQLite doesn't natively support the ``TRUNCATE`` command so behind the scenes
- ``DELETE FROM`` is used. It is recommended to call the ``VACUUM`` command
- after truncating a table. Migrations does not do this automatically.
-
-Executing Seed Classes
-======================
-
-This is the easy part. To seed your database, simply use the ``seeds run`` command:
-
-.. code-block:: bash
-
- $ bin/cake seeds run
-
-By default, Migrations will execute all available seed classes. If you would like to
-run a specific seed, simply pass in the seed name as an argument.
-You can use either the short name (without the ``Seed`` suffix) or the full name:
-
-.. code-block:: bash
-
- $ bin/cake seeds run User
- # or
- $ bin/cake seeds run UserSeed
-
-Both commands work identically.
-
-You can also run multiple seeds by separating them with commas:
-
-.. code-block:: bash
-
- $ bin/cake seeds run User,Permission,Log
- # or with full names
- $ bin/cake seeds run UserSeed,PermissionSeed,LogSeed
-
-You can also use the `-v` parameter for more output verbosity:
-
-.. code-block:: bash
-
- $ bin/cake seeds run -v
-
-The Migrations seed functionality provides a simple mechanism to easily and repeatably
-insert test data into your database, this is great for development environment
-sample data or getting state for demos.
diff --git a/docs/en/upgrading.md b/docs/en/upgrades/upgrading-from-4-x.md
similarity index 97%
rename from docs/en/upgrading.md
rename to docs/en/upgrades/upgrading-from-4-x.md
index c34d6c36a..0786baa8b 100644
--- a/docs/en/upgrading.md
+++ b/docs/en/upgrades/upgrading-from-4-x.md
@@ -103,12 +103,12 @@ $row = $stmt->fetch('assoc');
Seeds are now tracked in a `cake_seeds` table by default, preventing accidental re-runs.
Use `--force` to run a seed again, or `bin/cake seeds reset` to clear tracking.
-See [Database Seeding](seeding) for more details.
+See [Database Seeding](../guides/seeding) for more details.
### Check Constraints
Support for database check constraints via `addCheckConstraint()`.
-See [Writing Migrations](writing-migrations) for usage details.
+See [Writing Migrations](../guides/writing-migrations) for usage details.
### MySQL ALTER Options
diff --git a/docs/en/upgrading-to-builtin-backend.md b/docs/en/upgrades/upgrading-to-builtin-backend.md
similarity index 100%
rename from docs/en/upgrading-to-builtin-backend.md
rename to docs/en/upgrades/upgrading-to-builtin-backend.md
diff --git a/docs/en/upgrading.rst b/docs/en/upgrading.rst
deleted file mode 100644
index d413bff46..000000000
--- a/docs/en/upgrading.rst
+++ /dev/null
@@ -1,174 +0,0 @@
-Upgrading from 4.x to 5.x
-#########################
-
-Migrations 5.x includes significant changes from 4.x. This guide outlines
-the breaking changes and what you need to update when upgrading.
-
-Requirements
-============
-
-- **PHP 8.2+** is now required (was PHP 8.1+)
-- **CakePHP 5.3+** is now required
-- **Phinx has been removed** - The builtin backend is now the only supported backend
-
-If you were already using the builtin backend in 4.x (introduced in 4.3, default in 4.4),
-the upgrade should be straightforward. See :doc:`upgrading-to-builtin-backend` for more
-details on API differences between the phinx and builtin backends.
-
-Command Changes
-===============
-
-The phinx wrapper commands have been removed. The new command structure is:
-
-Migrations
-----------
-
-The migration commands remain unchanged:
-
-.. code-block:: bash
-
- bin/cake migrations migrate
- bin/cake migrations rollback
- bin/cake migrations status
- bin/cake migrations mark_migrated
- bin/cake migrations dump
-
-Seeds
------
-
-Seed commands have changed:
-
-.. code-block:: bash
-
- # 4.x # 5.x
- bin/cake migrations seed bin/cake seeds run
- bin/cake migrations seed --seed X bin/cake seeds run X
-
-The new seed commands are:
-
-- ``bin/cake seeds run`` - Run seed classes
-- ``bin/cake seeds run SeedName`` - Run a specific seed
-- ``bin/cake seeds status`` - Show seed execution status
-- ``bin/cake seeds reset`` - Reset seed execution tracking
-
-Maintaining Backward Compatibility
-----------------------------------
-
-If you need to maintain the old ``migrations seed`` command for existing scripts or
-CI/CD pipelines, you can add command aliases in your ``src/Application.php``::
-
- public function console(CommandCollection $commands): CommandCollection
- {
- $commands = $this->addConsoleCommands($commands);
-
- // Add backward compatibility alias
- $commands->add('migrations seed', \Migrations\Command\SeedCommand::class);
-
- return $commands;
- }
-
-Removed Classes and Namespaces
-==============================
-
-The following have been removed in 5.x:
-
-- ``Migrations\Command\Phinx\*`` - All phinx wrapper commands
-- ``Migrations\Command\MigrationsCommand`` - Use ``bin/cake migrations`` entry point
-- ``Migrations\Command\MigrationsSeedCommand`` - Use ``bin/cake seeds run``
-- ``Migrations\Command\MigrationsCacheBuildCommand`` - Schema cache is managed differently
-- ``Migrations\Command\MigrationsCacheClearCommand`` - Schema cache is managed differently
-- ``Migrations\Command\MigrationsCreateCommand`` - Use ``bin/cake bake migration``
-
-If you have code that directly references any of these classes, you will need to update it.
-
-API Changes
-===========
-
-Adapter Query Results
----------------------
-
-If your migrations use ``AdapterInterface::query()`` to fetch rows, the return type has
-changed from a phinx result to ``Cake\Database\StatementInterface``::
-
- // 4.x (phinx)
- $stmt = $this->getAdapter()->query('SELECT * FROM articles');
- $rows = $stmt->fetchAll();
- $row = $stmt->fetch();
-
- // 5.x (builtin)
- $stmt = $this->getAdapter()->query('SELECT * FROM articles');
- $rows = $stmt->fetchAll('assoc');
- $row = $stmt->fetch('assoc');
-
-New Features in 5.x
-===================
-
-5.x includes several new features:
-
-Seed Tracking
--------------
-
-Seeds are now tracked in a ``cake_seeds`` table by default, preventing accidental re-runs.
-Use ``--force`` to run a seed again, or ``bin/cake seeds reset`` to clear tracking.
-See :doc:`seeding` for more details.
-
-Check Constraints
------------------
-
-Support for database check constraints via ``addCheckConstraint()``.
-See :doc:`writing-migrations` for usage details.
-
-MySQL ALTER Options
--------------------
-
-Support for ``ALGORITHM`` and ``LOCK`` options on MySQL ALTER TABLE operations,
-allowing control over how MySQL performs schema changes.
-
-insertOrSkip() for Seeds
-------------------------
-
-New ``insertOrSkip()`` method for seeds to insert records only if they don't already exist,
-making seeds more idempotent.
-
-Foreign Key Constraint Naming
-=============================
-
-Starting in 5.x, when you use ``addForeignKey()`` without providing an explicit constraint
-name, migrations will auto-generate a name using the pattern ``{table}_{columns}``.
-
-Previously, MySQL would auto-generate constraint names (like ``articles_ibfk_1``), while
-PostgreSQL and SQL Server used migrations-generated names. Now all adapters use the same
-consistent naming pattern.
-
-**Impact on existing migrations:**
-
-If you have existing migrations that use ``addForeignKey()`` without explicit names, and
-later migrations that reference those constraints by name (e.g., in ``dropForeignKey()``),
-the generated names may differ between old and new migrations. This could cause
-``dropForeignKey()`` to fail if it's looking for a name that doesn't exist.
-
-**Recommendations:**
-
-1. For new migrations, you can rely on auto-generated names or provide explicit names
-2. If you have rollback issues with existing migrations, you may need to update them
- to use explicit constraint names
-3. The auto-generated names include conflict resolution - if ``{table}_{columns}`` already
- exists, a counter suffix is added (``_2``, ``_3``, etc.)
-
-**Name length limits:**
-
-Auto-generated names are truncated to respect database limits:
-
-- MySQL: 61 characters (64 - 3 for counter suffix)
-- PostgreSQL: 60 characters (63 - 3)
-- SQL Server: 125 characters (128 - 3)
-- SQLite: No limit
-
-Migration File Compatibility
-============================
-
-Your existing migration files should work without changes in most cases. The builtin backend
-provides the same API as phinx for common operations.
-
-If you encounter issues with existing migrations, please report them at
-https://github.com/cakephp/migrations/issues
diff --git a/docs/en/writing-migrations.md b/docs/en/writing-migrations.md
deleted file mode 100644
index fd2fd88bb..000000000
--- a/docs/en/writing-migrations.md
+++ /dev/null
@@ -1,2527 +0,0 @@
-# Writing Migrations
-
-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.
-
-## Creating a New Migration
-
-Let's start by creating a new migration with `bake`:
-
-```bash
-bin/cake bake migration
-```
-
-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.
-
-Bake will automatically creates a skeleton migration file with a single method:
-
-```php
- [
- 'style' => 'anonymous', // or 'traditional'
-],
-```
-
-This configuration also applies to seeds, allowing you to use consistent styling
-across your entire project.
-
-## The Change Method
-
-Migrations supports 'reversible migrations'. In many scenarios, you
-only need to define the `up` logic, and Migrations can figure out how to
-generate the rollback operations for you. For example:
-
-```php
-table('user_logins');
- $table->addColumn('user_id', 'integer')
- ->addColumn('created', 'datetime')
- ->create();
- }
-}
-```
-
-When executing this migration, Migrations 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, Migrations will
-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. Migrations 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
-Migrations, and will be automatically reversed:
-
-- Creating a table
-- Renaming a table
-- Adding a column
-- Renaming a column
-- Adding an index
-- Adding a foreign key
-- Adding a check constraint
-
-If a command cannot be reversed then Migrations 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:
-
-```php
-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 Migrations 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 Migrations 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 Migrations 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 Migrations 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 `BaseMigration`
-implementation.
-
-## Working With Tables
-
-The Table object enables 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:
-
-```php
-table('tableName');
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
-
- }
-}
-```
-
-You can then manipulate this table using the methods provided by the Table
-object.
-
-
-
-## 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](#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):
->
-> ``` php
-> $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:
-
-| 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 `date` columns:
-
-| Option | Description |
-|---------|---------------------------------------------|
-| default | set default value (use with `CURRENT_DATE`) |
-
-For `time` columns:
-
-| Option | Description |
-|----|----|
-| default | set default value (use with `CURRENT_TIME`) |
-| timezone | enable or disable the `with time zone` option *(only applies to Postgres)* |
-
-For `datetime` columns:
-
-| Option | Description |
-|----|----|
-| default | set default value (use with `CURRENT_TIMESTAMP`) |
-| timezone | enable or disable the `with time zone` option *(only applies to Postgres)* |
-
-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
-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
-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:
-
-```php
-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
-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
-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
-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 | set the table collation *(defaults to database collation)* |
-| row_format | MySQL | set the table row format |
-| engine | MySQL | define table engine *(defaults to \`\`InnoDB\`\`)* |
-| signed | MySQL | whether the primary key is `signed` *(defaults to \`\`true\`\`)* |
-| limit | MySQL | set the maximum length for the primary key |
-
-By default, the primary key is `signed`.
-To set it to be unsigned, pass the `signed` option with a `false`
-value, or enable the `Migrations.unsigned_primary_keys` and
-`Migrations.unsigned_ints` feature flags (see [Feature Flags](index#feature-flags)).
-Both flags should be used together so that foreign key columns match
-the primary keys they reference:
-
-```php
-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
-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](#adding-columns) for details.
-
-### MySQL ALTER TABLE Options
-
-::: info Added in version 5.0.0
-`ALGORITHM` and `LOCK` options were added in 5.0.0.
-:::
-
-When modifying tables in MySQL, you can control how the ALTER TABLE operation is
-performed using the `algorithm` and `lock` options. This is useful for performing
-zero-downtime schema changes on large tables in production environments.
-
-```php
-table('large_table');
- $table->addIndex(['status'], [
- 'name' => 'idx_status',
- ]);
- $table->update([
- 'algorithm' => 'INPLACE',
- 'lock' => 'NONE',
- ]);
- }
-}
-```
-
-Available `algorithm` values:
-
-| Algorithm | Description |
-|----|----|
-| DEFAULT | Let MySQL choose the algorithm (default behavior) |
-| INPLACE | Modify the table in place without copying data (when possible) |
-| COPY | Create a copy of the table with the changes (legacy method) |
-| INSTANT | Only modify metadata, no table rebuild (MySQL 8.0+, limited operations) |
-
-Available `lock` values:
-
-| Lock | Description |
-|-----------|----------------------------------------------------------|
-| DEFAULT | Use minimal locking for the algorithm (default behavior) |
-| NONE | Allow concurrent reads and writes during the operation |
-| SHARED | Allow concurrent reads but block writes |
-| EXCLUSIVE | Block all reads and writes during the operation |
-
-> [!NOTE]
-> Not all operations support all algorithm/lock combinations. MySQL will raise
-> an error if the requested combination is not possible for the operation.
-> The `INSTANT` algorithm is only available in MySQL 8.0+ and only for specific
-> operations like adding columns at the end of a table.
-
-> [!WARNING]
-> Using `ALGORITHM=INPLACE, LOCK=NONE` does not guarantee zero-downtime for
-> all operations. Some operations may still require a table copy or exclusive lock.
-> Always test schema changes on a staging environment first.
-
-### Table Partitioning
-
-Migrations supports table partitioning for MySQL and PostgreSQL. Partitioning helps
-manage large tables by splitting them into smaller, more manageable pieces.
-
-> [!NOTE]
-> Partition columns must be included in the primary key for MySQL. SQLite does
-> not support partitioning. MySQL's `RANGE` and `LIST` types only work with
-> integer columns - use `RANGE COLUMNS` and `LIST COLUMNS` for DATE/STRING columns.
-
-#### RANGE Partitioning
-
-RANGE partitioning is useful when you want to partition by numeric ranges. For MySQL,
-use `TYPE_RANGE` with integer columns or expressions, and `TYPE_RANGE_COLUMNS` for
-DATE/DATETIME/STRING columns:
-
-```php
-table('orders', [
- 'id' => false,
- 'primary_key' => ['id', 'order_date'],
- ]);
- $table->addColumn('id', 'integer', ['identity' => true])
- ->addColumn('order_date', 'date')
- ->addColumn('amount', 'decimal', ['precision' => 10, 'scale' => 2])
- ->partitionBy(Partition::TYPE_RANGE_COLUMNS, 'order_date')
- ->addPartition('p2022', '2023-01-01')
- ->addPartition('p2023', '2024-01-01')
- ->addPartition('p2024', '2025-01-01')
- ->addPartition('pmax', 'MAXVALUE')
- ->create();
- }
-}
-```
-
-#### LIST Partitioning
-
-LIST partitioning is useful when you want to partition by discrete values. For MySQL,
-use `TYPE_LIST` with integer columns and `TYPE_LIST_COLUMNS` for STRING columns:
-
-```php
-table('customers', [
- 'id' => false,
- 'primary_key' => ['id', 'region'],
- ]);
- $table->addColumn('id', 'integer', ['identity' => true])
- ->addColumn('region', 'string', ['limit' => 20])
- ->addColumn('name', 'string')
- ->partitionBy(Partition::TYPE_LIST_COLUMNS, 'region')
- ->addPartition('p_americas', ['US', 'CA', 'MX', 'BR'])
- ->addPartition('p_europe', ['UK', 'DE', 'FR', 'IT'])
- ->addPartition('p_asia', ['JP', 'CN', 'IN', 'KR'])
- ->create();
- }
-}
-```
-
-#### HASH Partitioning
-
-HASH partitioning distributes data evenly across a specified number of partitions:
-
-```php
-table('sessions');
- $table->addColumn('user_id', 'integer')
- ->addColumn('data', 'text')
- ->partitionBy(Partition::TYPE_HASH, 'user_id', ['count' => 8])
- ->create();
- }
-}
-```
-
-#### KEY Partitioning (MySQL only)
-
-KEY partitioning is similar to HASH but uses MySQL's internal hashing function:
-
-```php
-table('cache', [
- 'id' => false,
- 'primary_key' => ['cache_key'],
- ]);
- $table->addColumn('cache_key', 'string', ['limit' => 255])
- ->addColumn('value', 'binary')
- ->partitionBy(Partition::TYPE_KEY, 'cache_key', ['count' => 16])
- ->create();
- }
-}
-```
-
-#### Partitioning with Expressions
-
-You can partition by expressions using the `Literal` class:
-
-```php
-table('events', [
- 'id' => false,
- 'primary_key' => ['id', 'created_at'],
- ]);
- $table->addColumn('id', 'integer', ['identity' => true])
- ->addColumn('created_at', 'datetime')
- ->partitionBy(Partition::TYPE_RANGE, Literal::from('YEAR(created_at)'))
- ->addPartition('p2022', 2023)
- ->addPartition('p2023', 2024)
- ->addPartition('pmax', 'MAXVALUE')
- ->create();
- }
-}
-```
-
-#### Modifying Partitions on Existing Tables
-
-You can add or drop partitions on existing partitioned tables:
-
-```php
-table('orders')
- ->addPartitionToExisting('p2025', '2026-01-01')
- ->update();
- }
-
- public function down(): void
- {
- // Drop the partition
- $this->table('orders')
- ->dropPartition('p2025')
- ->update();
- }
-}
-```
-
-### 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
-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
-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
-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
-table('tags');
- $table->addColumn('short_name', 'string', ['limit' => 30])
- ->update();
- }
-}
-```
-
-### Changing Column Attributes
-
-There are two methods for modifying existing columns:
-
-#### Updating Columns (Recommended)
-
-To modify specific column attributes while preserving others, use the `updateColumn()` method.
-This method automatically preserves unspecified attributes like defaults, nullability, limits, etc.:
-
-```php
-table('users');
- // Make email nullable, preserving all other attributes
- $users->updateColumn('email', null, ['null' => true])
- ->save();
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- $users = $this->table('users');
- $users->updateColumn('email', null, ['null' => false])
- ->save();
- }
-}
-```
-
-You can pass `null` as the column type to preserve the existing type, or specify a new type:
-
-```php
-// Preserve type and other attributes, only change nullability
-$table->updateColumn('email', null, ['null' => true]);
-
-// Change type to biginteger, preserve default and other attributes
-$table->updateColumn('user_id', 'biginteger');
-
-// Change default value, preserve everything else
-$table->updateColumn('status', null, ['default' => 'active']);
-```
-
-The following attributes are automatically preserved by `updateColumn()`:
-
-- Default values
-- NULL/NOT NULL constraint
-- Column limit/length
-- Decimal scale/precision
-- Comments
-- Signed/unsigned (for numeric types)
-- Collation and encoding
-- Enum/set values
-
-#### Changing Columns (Traditional)
-
-To completely replace a column definition, use the `changeColumn()` method.
-This method requires you to specify all desired column attributes.
-See [Adding Columns](#adding-columns) and [Valid Column Options](#valid-column-options) for allowed values:
-
-```php
-table('users');
- // Must specify all attributes
- $users->changeColumn('email', 'string', [
- 'limit' => 255,
- 'null' => true,
- 'default' => null,
- ])
- ->save();
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
-
- }
-}
-```
-
-You can enable attribute preservation with `changeColumn()` by passing
-`'preserveUnspecified' => true` in the options:
-
-```php
-$table->changeColumn('email', 'string', [
- 'null' => true,
- 'preserveUnspecified' => true,
-]);
-```
-
-> [!NOTE]
-> For most use cases, `updateColumn()` is recommended as it is safer and requires
-> less code. Use `changeColumn()` when you need to completely redefine a column
-> or when working with legacy code that expects the traditional behavior.
-
-### Working With Indexes
-
-To add an index to a table you can simply call the `addIndex()` method on the
-table object:
-
-```php
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
- {
-
- }
-}
-```
-
-::: info Added 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
-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
-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
-table('articles');
- $table->addForeignKey(
- $this->foreignKey()
- ->setColumns('user_id')
- ->setReferencedTable('users')
- ->setReferencedColumns('user_id')
- ->setDelete(ForeignKey::CASCADE)
- ->setName('article_user_fk')
- )
- ->save();
- }
-}
-```
-
-::: info Added in version 4.6.0
-The `foreignKey` method was added.
-:::
-
-We can also easily check if a foreign key exists:
-
-```php
-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
-table('tag_relationships');
- $table->dropForeignKey('tag_id')->save();
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
-
- }
-}
-```
-
-### Working With Check Constraints
-
-::: info Added in version 5.0.0
-Check constraints were added in 5.0.0.
-:::
-
-Check constraints allow you to enforce data validation rules at the database level.
-They are particularly useful for ensuring data integrity across your application.
-
-> [!NOTE]
-> Check constraints are supported by MySQL 8.0.16+, PostgreSQL, and SQLite.
-> SQL Server support is planned for a future release.
-
-#### Adding a Check Constraint
-
-You can add a check constraint to a table using the `addCheckConstraint()` method:
-
-```php
-table('products');
- $table->addColumn('price', 'decimal', ['precision' => 10, 'scale' => 2])
- ->addCheckConstraint('price_positive', 'price > 0')
- ->save();
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- $table = $this->table('products');
- $table->dropCheckConstraint('price_positive')
- ->save();
- }
-}
-```
-
-The first argument is the constraint name, and the second is the SQL expression
-that defines the constraint. The expression should evaluate to a boolean value.
-
-#### Using the CheckConstraint Fluent Builder
-
-For more complex scenarios, you can use the `checkConstraint()` method to get
-a fluent builder:
-
-```php
-table('users');
- $table->addColumn('age', 'integer')
- ->addColumn('status', 'string', ['limit' => 20])
- ->addCheckConstraint(
- $this->checkConstraint()
- ->setName('age_valid')
- ->setExpression('age >= 18 AND age <= 120')
- )
- ->addCheckConstraint(
- $this->checkConstraint()
- ->setName('status_valid')
- ->setExpression("status IN ('active', 'inactive', 'pending')")
- )
- ->save();
- }
-}
-```
-
-#### Auto-Generated Constraint Names
-
-If you don't specify a constraint name, one will be automatically generated based
-on the table name and expression hash:
-
-```php
-table('inventory');
- $table->addColumn('quantity', 'integer')
- // Name will be auto-generated like 'inventory_chk_a1b2c3d4'
- ->addCheckConstraint(
- $this->checkConstraint()
- ->setExpression('quantity >= 0')
- )
- ->save();
- }
-}
-```
-
-#### Complex Check Constraints
-
-Check constraints can reference multiple columns and use complex SQL expressions:
-
-```php
-table('date_ranges');
- $table->addColumn('start_date', 'date')
- ->addColumn('end_date', 'date')
- ->addColumn('discount', 'decimal', ['precision' => 5, 'scale' => 2])
- ->addCheckConstraint('valid_date_range', 'end_date >= start_date')
- ->addCheckConstraint('valid_discount', 'discount BETWEEN 0 AND 100')
- ->save();
- }
-}
-```
-
-#### Checking if a Check Constraint Exists
-
-You can verify if a check constraint exists using the `hasCheckConstraint()` method:
-
-```php
-table('products');
- $exists = $table->hasCheckConstraint('price_positive');
- if ($exists) {
- // do something
- } else {
- $table->addCheckConstraint('price_positive', 'price > 0')
- ->save();
- }
- }
-}
-```
-
-#### Dropping a Check Constraint
-
-To remove a check constraint, use the `dropCheckConstraint()` method with the
-constraint name:
-
-```php
-table('products');
- $table->dropCheckConstraint('price_positive')
- ->save();
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- $table = $this->table('products');
- $table->addCheckConstraint('price_positive', 'price > 0')
- ->save();
- }
-}
-```
-
-> [!NOTE]
-> Like other table operations, `dropCheckConstraint()` requires `save()`
-> to be called to execute the change.
-
-#### Database-Specific Behavior
-
-**MySQL (8.0.16+)**
-
-Check constraints are fully supported. MySQL stores constraint metadata in the
-`INFORMATION_SCHEMA.CHECK_CONSTRAINTS` table.
-
-**PostgreSQL**
-
-Check constraints are fully supported and stored in the `pg_constraint` catalog.
-PostgreSQL allows the most flexible expressions in check constraints.
-
-**SQLite**
-
-Check constraints are supported but with some limitations. SQLite does not support
-`ALTER TABLE` operations for check constraints, so adding or dropping constraints
-requires recreating the entire table. This is handled automatically by the adapter.
-
-**SQL Server**
-
-Check constraint support for SQL Server is planned for a future release.
-
-### Determining Whether a Table Exists
-
-You can determine whether or not a table exists by using the `hasTable()`
-method:
-
-```php
-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
-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
-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
-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
-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.:
-
-```bash
-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.
-
-### 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:
-
-```php
-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(): void
- {
-
- }
-}
-```
-
-## Checking Columns
-
-`BaseMigration` also provides methods for introspecting the current schema,
-allowing you to conditionally make changes to schema, or read data.
-Schema is inspected **when the migration is run**.
-
-### 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:
-
-```php
-table('users')->getColumns();
- ...
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- ...
- }
-}
-```
-
-### Get a column by name
-
-To retrieve one table column, simply create a `table` object and call the
-`getColumn()` method. This method will return a Column class with basic info
-or NULL when the column doesn't exist. Example below:
-
-```php
-table('users')->getColumn('email');
- ...
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- ...
- }
-}
-```
-
-### Checking whether a column exists
-
-You can check if a table already has a certain column by using the
-`hasColumn()` method:
-
-```php
-table('user');
- $column = $table->hasColumn('username');
-
- if ($column) {
- // 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
-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
-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
-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
-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.:
-
-```bash
-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.
-
-### 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:
-
-```php
-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(): void
- {
-
- }
-}
-```
-
-## Checking Columns
-
-`BaseMigration` also provides methods for introspecting the current schema,
-allowing you to conditionally make changes to schema, or read data.
-Schema is inspected **when the migration is run**.
-
-### 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:
-
-```php
-table('users')->getColumns();
- ...
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- ...
- }
-}
-```
-
-### Get a column by name
-
-To retrieve one table column, simply create a `table` object and call the
-`getColumn()` method. This method will return a Column class with basic info
-or NULL when the column doesn't exist. Example below:
-
-```php
-table('users')->getColumn('email');
- ...
- }
-
- /**
- * Migrate Down.
- */
- public function down(): void
- {
- ...
- }
-}
-```
-
-### Checking whether a column exists
-
-You can check if a table already has a certain column by using the
-`hasColumn()` method:
-
-```php
-table('user');
- $column = $table->hasColumn('username');
-
- if ($column) {
- // do something
- }
-
- }
-}
-```
-
-### Changing templates
-
-See [Custom Seed Migration Templates](seeding#custom-seed-migration-templates) for how to customize the templates
-used to generate migrations.
-
-## Database-Specific Limitations
-
-While Migrations aims to provide a database-agnostic API, some features have
-database-specific limitations or are not available on all platforms.
-
-### SQL Server
-
-The following features are not supported on SQL Server:
-
-**Check Constraints**
-
-Check constraints are not currently implemented for SQL Server. Attempting to
-use `addCheckConstraint()` or `dropCheckConstraint()` will throw a
-`BadMethodCallException`.
-
-**Table Comments**
-
-SQL Server does not support table comments. Attempting to use `changeComment()`
-will throw a `BadMethodCallException`.
-
-**INSERT IGNORE / insertOrSkip()**
-
-SQL Server does not support the `INSERT IGNORE` syntax used by `insertOrSkip()`.
-This method will throw a `RuntimeException` on SQL Server. Use `insertOrUpdate()`
-instead for upsert operations, which uses `MERGE` statements on SQL Server.
-
-### SQLite
-
-**Foreign Key Names**
-
-SQLite does not support named foreign keys. The foreign key constraint name option
-is ignored when creating foreign keys on SQLite.
-
-**Table Comments**
-
-SQLite does not support table comments directly. Comments are stored as metadata
-but not in the database itself.
-
-**Check Constraint Modifications**
-
-SQLite does not support `ALTER TABLE` operations for check constraints. Adding or
-dropping check constraints requires recreating the entire table, which is handled
-automatically by the adapter.
-
-**Table Partitioning**
-
-SQLite does not support table partitioning.
-
-### PostgreSQL
-
-**KEY Partitioning**
-
-PostgreSQL does not support MySQL's `KEY` partitioning type. Use `HASH`
-partitioning instead for similar distribution behavior.
-
-### MySQL/MariaDB
-
-**insertOrUpdate() Conflict Columns**
-
-For MySQL, the `$conflictColumns` parameter in `insertOrUpdate()` is ignored
-because MySQL's `ON DUPLICATE KEY UPDATE` automatically applies to all unique
-constraints. PostgreSQL and SQLite require this parameter to be specified.
-
-**MariaDB GIS/Geometry**
-
-Some geometry column features may not work correctly on MariaDB due to differences
-in GIS implementation compared to MySQL.
diff --git a/docs/en/writing-migrations.rst b/docs/en/writing-migrations.rst
deleted file mode 100644
index e69de29bb..000000000