Claude Code Plugins

Community-maintained marketplace

Feedback

symfony:doctrine-migrations

@MakFly/superpowers-symfony
14
0

Create and manage Doctrine migrations for schema versioning; handle migration dependencies, rollbacks, and production deployment

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name symfony:doctrine-migrations
description Create and manage Doctrine migrations for schema versioning; handle migration dependencies, rollbacks, and production deployment

Doctrine Migrations

Installation

composer require doctrine/doctrine-migrations-bundle

Basic Commands

# Generate migration from entity changes
bin/console make:migration

# Or using doctrine directly
bin/console doctrine:migrations:diff

# Run pending migrations
bin/console doctrine:migrations:migrate

# Check status
bin/console doctrine:migrations:status

# List all migrations
bin/console doctrine:migrations:list

Migration Workflow

1. Modify Entity

<?php
// src/Entity/User.php

#[ORM\Entity]
class User
{
    // Add new property
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $avatarUrl = null;
}

2. Generate Migration

bin/console make:migration

Generated file:

<?php
// migrations/Version20240115120000.php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240115120000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Add avatar_url column to user table';
    }

    public function up(Schema $schema): void
    {
        $this->addSql('ALTER TABLE user ADD avatar_url VARCHAR(255) DEFAULT NULL');
    }

    public function down(Schema $schema): void
    {
        $this->addSql('ALTER TABLE user DROP avatar_url');
    }
}

3. Run Migration

# Dry run first
bin/console doctrine:migrations:migrate --dry-run

# Execute
bin/console doctrine:migrations:migrate

Advanced Migrations

Data Migration

public function up(Schema $schema): void
{
    // Schema change
    $this->addSql('ALTER TABLE user ADD status VARCHAR(20) NOT NULL DEFAULT \'active\'');

    // Data migration
    $this->addSql("UPDATE user SET status = 'inactive' WHERE last_login < DATE_SUB(NOW(), INTERVAL 1 YEAR)");
}

Multiple Statements

public function up(Schema $schema): void
{
    $this->addSql('ALTER TABLE order ADD customer_id INT DEFAULT NULL');
    $this->addSql('ALTER TABLE order ADD CONSTRAINT FK_ORDER_CUSTOMER FOREIGN KEY (customer_id) REFERENCES customer (id)');
    $this->addSql('CREATE INDEX IDX_ORDER_CUSTOMER ON order (customer_id)');
}

Using Schema Object

public function up(Schema $schema): void
{
    $table = $schema->getTable('user');
    $table->addColumn('avatar_url', 'string', [
        'length' => 255,
        'notnull' => false,
    ]);
    $table->addIndex(['avatar_url'], 'idx_user_avatar');
}

Conditional Migration

public function up(Schema $schema): void
{
    // Only for MySQL
    if ($this->connection->getDatabasePlatform()->getName() === 'mysql') {
        $this->addSql('ALTER TABLE user ENGINE=InnoDB');
    }
}

public function preUp(Schema $schema): void
{
    // Check before running
    $count = $this->connection->fetchOne('SELECT COUNT(*) FROM user WHERE status IS NULL');
    if ($count > 0) {
        throw new \Exception('Cannot migrate: null status values exist');
    }
}

public function postUp(Schema $schema): void
{
    // Verify after running
    $this->connection->executeQuery('ANALYZE TABLE user');
}

Rollback

# Rollback last migration
bin/console doctrine:migrations:migrate prev

# Rollback to specific version
bin/console doctrine:migrations:migrate Version20240101000000

# Rollback all
bin/console doctrine:migrations:migrate first

Version Control

# Mark migration as executed (without running)
bin/console doctrine:migrations:version --add Version20240115120000

# Remove from version table
bin/console doctrine:migrations:version --delete Version20240115120000

Configuration

# config/packages/doctrine_migrations.yaml
doctrine_migrations:
    migrations_paths:
        'DoctrineMigrations': '%kernel.project_dir%/migrations'
    all_or_nothing: true
    transactional: true
    check_database_platform: true
    organize_migrations: none # none, year, year_and_month

Production Best Practices

1. Always Test Migrations

# On staging
bin/console doctrine:migrations:migrate --dry-run
bin/console doctrine:migrations:migrate

# Verify schema
bin/console doctrine:schema:validate

2. Backup Before Migration

# MySQL
mysqldump -u user -p database > backup_$(date +%Y%m%d_%H%M%S).sql

# PostgreSQL
pg_dump -U user database > backup_$(date +%Y%m%d_%H%M%S).sql

3. Non-Blocking Migrations

For large tables, consider:

// Instead of adding NOT NULL column directly
public function up(Schema $schema): void
{
    // Step 1: Add nullable column
    $this->addSql('ALTER TABLE user ADD status VARCHAR(20) DEFAULT NULL');
}

// In next migration after data backfill
public function up(Schema $schema): void
{
    // Step 2: Make it NOT NULL
    $this->addSql('ALTER TABLE user MODIFY status VARCHAR(20) NOT NULL');
}

4. CI/CD Integration

# .github/workflows/deploy.yml
- name: Run migrations
  run: |
    php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration

Schema Validation

# Validate mapping
bin/console doctrine:schema:validate

# Compare schema with entities
bin/console doctrine:schema:update --dump-sql

Common Issues

Migration Already Executed

# Force re-run (dangerous!)
bin/console doctrine:migrations:execute Version20240115120000 --up

# Or mark as not executed
bin/console doctrine:migrations:version --delete Version20240115120000
bin/console doctrine:migrations:migrate

Conflicting Migrations

When multiple developers create migrations:

  1. Pull latest changes
  2. Rollback your migration: bin/console doctrine:migrations:migrate prev
  3. Delete your migration file
  4. Regenerate: bin/console make:migration