Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Entity Migrator Laravel Package

draw/entity-migrator

Laravel package for migrating and transforming entities between data sources. Helps map fields, move records safely, and run repeatable migration workflows with configurable steps for imports, upgrades, and refactors.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps for Laravel Adoption

  1. Install Dependencies:

    composer require draw/entity-migrator symfony/messenger doctrine/dbal spatie/laravel-messenger
    
    • Use spatie/laravel-messenger to bridge Symfony Messenger with Laravel Queues.
  2. Configure Doctrine DBAL: Register DBAL as a Laravel service provider:

    // config/app.php
    'providers' => [
        Doctrine\DBAL\Bridge\Doctrine\DBALServiceProvider::class,
    ],
    

    Configure DBAL in config/dbal.php to match your Laravel database connection.

  3. First Migration Example: Create a migration class targeting an Eloquent model:

    namespace App\Migrations;
    
    use Draw\EntityMigrator\Migration;
    use App\Models\User;
    
    class AddUserVerificationFields extends Migration
    {
        public function up(): void
        {
            $this->addField(User::class, 'email_verified_at', 'timestamp', ['nullable' => true]);
            $this->updateData(User::class, function ($entity) {
                // Custom data transformation logic
                $entity->email_verified_at = now();
            });
        }
    
        public function down(): void
        {
            $this->removeField(User::class, 'email_verified_at');
        }
    }
    
  4. Run via Artisan: Create a custom Artisan command:

    php artisan make:command EntityMigrateCommand
    

    Update the command to use the migrator:

    use Draw\EntityMigrator\Migrator;
    use Doctrine\DBAL\Connection;
    
    class EntityMigrateCommand extends Command
    {
        protected $signature = 'entity:migrate {migration?}';
        protected $description = 'Run entity migrations';
    
        public function handle()
        {
            $migrator = new Migrator(
                new Connection($this->app['db']->connection()->getDoctrineConnection()),
                new \Spatie\Messenger\MessageBus()
            );
    
            $migration = $this->argument('migration') ? new $this->argument('migration') : null;
            $migrator->migrate($migration);
        }
    }
    

    Execute:

    php artisan entity:migrate App\Migrations\AddUserVerificationFields
    
  5. Where to Look First:

    • src/Migrations/: Store all custom migration classes.
    • config/entity_migrator.php: Configure global settings (e.g., lock timeout, batch sizes).
    • app/Console/Kernel.php: Register the custom command in $commands.

Implementation Patterns

1. Hybrid Migration Workflows

Combine Laravel’s Schema migrations with EntityMigrator for complex data changes:

// Schema migration (simple)
Schema::table('users', function (Blueprint $table) {
    $table->string('new_column')->nullable();
});

// Data migration (complex)
$migrator = new Migrator($dbalConnection, $messageBus);
$migrator->migrate(new class extends Migration {
    public function up(): void {
        $this->updateData(User::class, function ($user) {
            $user->new_column = strtolower($user->email);
        });
    }
});

2. Asynchronous Batch Processing

Use Symfony Messenger to process large datasets in batches:

// Define a message handler
class ProcessUserBatchHandler
{
    public function __invoke(ProcessUserBatch $message)
    {
        $users = User::where('id', '>=', $message->offset)
                     ->limit($message->batchSize)
                     ->get();

        foreach ($users as $user) {
            $user->update(['status' => 'processed']);
        }
    }
}

// Dispatch batches via migration
public function up(): void
{
    $batchSize = 1000;
    $total = User::count();

    for ($offset = 0; $offset < $total; $offset += $batchSize) {
        $this->dispatch(new ProcessUserBatch($offset, $batchSize));
    }
}

3. Workflow-Driven Migrations

Leverage Symfony Workflow for multi-step migrations with approval gates:

// Define workflow transitions
$workflow = new Workflow(
    new PlaceTransition('validate'),
    new PlaceTransition('migrate'),
    new PlaceTransition('notify')
);

// Integrate with Laravel events
event(new MigrationWorkflowTransitioned('migrate', [
    'users_processed' => 5000,
    'status' => 'in_progress'
]));

4. Locking for Concurrent Migrations

Prevent race conditions during migrations:

use Symfony\Component\Lock\LockFactory;

// In your migration class
public function up(): void
{
    $lockFactory = new LockFactory();
    $lock = $lockFactory->createLock('user_migration_lock', 3600); // 1-hour lock

    if (!$lock->acquire()) {
        throw new \RuntimeException('Migration already in progress');
    }

    try {
        // Migration logic here
    } finally {
        $lock->release();
    }
}

5. Testing Migrations

Use draw/tester to mock data transformations:

public function testMigration()
{
    $migrator = new Migrator($this->createMockConnection(), $this->createMockBus());
    $migration = new AddUserVerificationFields();

    $this->assertNull($migration->down()); // Test rollback

    // Mock data for testing
    $tester = new \Draw\Tester\MigrationTester($migrator);
    $tester->assertMigration($migration, [
        'users' => [
            ['id' => 1, 'email_verified_at' => null],
            ['id' => 2, 'email_verified_at' => null],
        ],
        'expected' => [
            ['id' => 1, 'email_verified_at' => now()],
            ['id' => 2, 'email_verified_at' => now()],
        ],
    ]);
}

6. Integration with Laravel Queues

Offload migration steps to Laravel’s queue system:

// Dispatch a migration step as a job
$this->dispatch(new MigrateUsersJob());

// Job implementation
class MigrateUsersJob implements ShouldQueue
{
    public function handle()
    {
        $migrator = app(Migrator::class);
        $migrator->migrate(new class extends Migration {
            public function up(): void {
                $this->updateData(User::class, function ($user) {
                    $user->status = 'migrated';
                });
            }
        });
    }
}

Gotchas and Tips

Pitfalls

  1. Doctrine vs. Laravel Database Abstraction:

    • Issue: EntityMigrator uses Doctrine DBAL, which may not align with Laravel’s Schema builder.
    • Fix: Use DBAL for data migrations and Laravel’s Schema for schema changes to avoid conflicts.
    • Example:
      // Avoid mixing:
      // Schema::table('users', ...); // Laravel
      // $this->addField(User::class, ...); // DBAL (EntityMigrator)
      
  2. Locking Overhead:

    • Issue: Symfony’s Lock component can cause timeouts or blocking in high-concurrency environments.
    • Fix: Set a reasonable lock timeout (e.g., 3600 seconds) and avoid locking during peak hours.
    • Tip: Use database-level locks (e.g., SELECT ... FOR UPDATE) for critical sections.
  3. Rollback Limitations:

    • Issue: The package lacks native rollback support for data migrations.
    • Fix: Implement a custom down() method or use Laravel’s migration rollbacks for schema changes.
    • Example:
      public function down(): void
      {
          // Fallback to Laravel's rollback for schema
          Schema::table('users', function (Blueprint $table) {
              $table->dropColumn('email_verified_at');
          });
      
          // Manual data rollback (if needed)
          User::whereNotNull('email_verified_at')->update(['email_verified_at' => null]);
      }
      
  4. Symfony Messenger Complexity:

    • Issue: Overhead of setting up Symfony Messenger for simple migrations.
    • Fix: Use Laravel Queues directly for async steps and reserve Messenger for workflow-heavy migrations.
    • Tip: Start with synchronous migrations and introduce Messenger only when needed.
  5. Transaction Isolation:

    • Issue: Long-running migrations may hold locks too long, causing deadlocks.
    • Fix: Break migrations into smaller transactions or use optimistic locking.
    • Example:
      DB::transaction(function () {
          // Process 100 records at a time
          for ($i = 0; $i < 100; $i++) {
              DB::transaction(function () use ($i) {
                  $user = User::
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor