doctrine/migrations
Doctrine Migrations manages database schema changes via versioned migrations for PHP projects. Generate, run, and track migration scripts, integrate with Doctrine DBAL/ORM, and safely evolve schemas across environments with robust CLI tooling and documentation.
Install the package via Composer:
composer require doctrine/dbal doctrine/migrations
(Laravel already includes doctrine/dbal via Eloquent, so only doctrine/migrations is needed.)
Publish the migration configuration (optional but recommended):
php artisan vendor:publish --provider="Doctrine\Migrations\ServiceProvider"
This creates a config/doctrine_migrations.php file.
Generate your first migration:
php artisan doctrine:migrations:generate --name="CreateUsersTable" --path="database/migrations"
This creates a new migration file in database/migrations/ with a timestamp prefix.
Run the migration:
php artisan doctrine:migrations:migrate
database/migrations/ file (e.g., add a new column to users table).php artisan doctrine:migrations:migrate
Creating Migrations:
doctrine:migrations:generate for new migrations.// database/migrations/YYYYMMDDHHMMSS_CreateUsersTable.php
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE users (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id))');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE users');
}
Running Migrations:
php artisan doctrine:migrations:migrate
php artisan doctrine:migrations:migrate --to=20230101000000
php artisan doctrine:migrations:migrate --direction=down
Generating Migrations from Schema Changes:
doctrine:migrations:diff to auto-generate migrations from existing schema changes:
php artisan doctrine:migrations:diff --path="database/migrations"
Executing Raw SQL:
doctrine:migrations:execute:
php artisan doctrine:migrations:execute --sql="ALTER TABLE users ADD COLUMN email VARCHAR(255)"
config/app.php under providers:
Doctrine\Migrations\ServiceProvider::class,
config/doctrine_migrations.php:
'migrations_paths' => [
'DoctrineMigrations' => __DIR__.'/../database/migrations',
],
'table_name' => 'migrations_versions',
'connection' => 'mysql', // Use your Laravel DB connection
Dependency Injection:
Migration class in Laravel services:
use Doctrine\Migrations\Migration;
use Doctrine\DBAL\Schema\Schema;
class CustomMigration extends Migration
{
public function up(Schema $schema): void
{
// Custom logic
}
}
Custom Commands:
Doctrine\Migrations\Tools\Console\Command\AbstractCommand to create custom CLI tools.Event Listeners:
preMigration, postMigration) via Doctrine\Migrations\Event\Events.Schema Name Handling:
schema_name.table_name), ensure the DiffGenerator is configured to handle them:
$diffGenerator = new DiffGenerator();
$diffGenerator->setSchemaName('schema_name'); // Explicitly set schema
config/doctrine_migrations.php to include schema names in diffs:
'diff_generator' => [
'schema_name' => 'your_schema',
],
Down Migration Failures:
down() migrations locally. Use --dry-run to preview:
php artisan doctrine:migrations:migrate --dry-run
down() migrations to ensure rollback consistency.Connection Issues:
connection in config/doctrine_migrations.php matches your Laravel .env DB settings.--verbose flag for detailed output:
php artisan doctrine:migrations:migrate --verbose
Migration Table Conflicts:
migrations_versions) might conflict with Laravel’s own migrations. Rename it in config:
'table_name' => 'custom_migration_versions',
Large Migrations:
Enable SQL Logging:
config/doctrine_migrations.php:
'logging' => true,
'logging_level' => 'DEBUG',
storage/logs/laravel.log.Check Migration Status:
migrations_versions table directly:
SELECT * FROM migrations_versions ORDER BY version_number DESC;
Reset Migrations:
php artisan doctrine:migrations:execute --sql="DROP TABLE migrations_versions"
php artisan doctrine:migrations:migrate
Custom Migration Classes:
Doctrine\Migrations\AbstractMigration for reusable logic:
class BaseMigration extends AbstractMigration
{
protected function createTable(string $tableName, array $columns): void
{
$this->addSql(sprintf('CREATE TABLE %s (%s)', $tableName, implode(', ', $columns)));
}
}
Custom Diff Generator:
Doctrine\Migrations\Tools\Console\Command\DiffCommand to filter tables/schemas:
protected function getDiffGenerator(): DiffGenerator
{
$generator = new DiffGenerator();
$generator->setFilterTableExpression('/^(?!ignored_).*/'); // Skip ignored tables
return $generator;
}
Pre/Post Migration Hooks:
$eventManager = $migration->getEventManager();
$eventManager->addEventListener(
Events::preMigration,
function (PreMigrationEventArgs $event) {
// Logic before migration
}
);
Custom SQL Formatting:
SqlFormatter to customize SQL output (e.g., for readability):
$formatter = new SqlFormatter();
$formatter->setLineLength(120); // Wider lines
$migration->setSqlFormatter($formatter);
Artisan Command Namespace:
App\Console\Commands and registered in app/Console/Kernel.php.Database Transactions:
DB::transaction() may conflict with Doctrine’s migrations. Use doctrine:migrations:migrate --all-or-nothing to wrap migrations in a transaction.Schema Introspection:
Schema::getTableName() matches your actual table names (e.g., singular/plural conventions).Environment-Specific Migrations:
if (app()->environment('local')) {
$this->addSql('CREATE INDEX idx_users_name ON users(name)');
}
How can I help you explore Laravel packages today?