Home > Mobile >  Laravel Eloquent migration to swap data from columns
Laravel Eloquent migration to swap data from columns

Time:08-13

I have the following migration that aims to make two new columns (parent_id; parent_type) and fill them with data from four current columns (page_id; post_id; global_id; event_id) from the same table. The latter four columns cannot be at the same time non-null, if one has a value, the others are null, therefore there needs to be some kind of if functionality.

Is this the right way to do it? I have run this migration, I have replaced the places in the codebase where there was a refference to any of the four previous columns and replaced them with their appropiate parent_id/parent_type but Laravel is still looking for block.page_id.

public function up()
  {
    Schema::table('blocks', function (Blueprint $table) {
      $table->integer('parent_id')->nullable()->after('type');
      $table->string('parent_type')->nullable()->after('parent_id');
    });

    $blocks = Block::select('id', 'page_id', 'post_id', 'event_id', 'global_id')->get();

    foreach ($blocks as $block) {
      if ($block['post_id'] !== null) {
        $block->parent_id = $block['post_id'];
        $block->parent_type = 'post';
      } elseif ($block['page_id'] !== null) {
        $block->parent_id = $block['page_id'];
        $block->parent_type = 'page';
      } elseif ($block['event_id'] !== null) {
        $block->parent_id = $block['event_id'];
        $block->parent_type = 'post';
      } elseif ($block['global_id'] !== null) {
        $block->parent_id = $block['global_id'];
        $block->parent_type = 'global';
      } else {
        $block->parent_id = null;
      }
      $block->save();
    }
  }

After this migration, I have another one that drops the four columns:

public function up()
  {
    Schema::table('blocks', function (Blueprint $table) {
      $table->dropColumn('page_id');
      $table->dropColumn('post_id');
      $table->dropColumn('event_id');
      $table->dropColumn('global_id');
    });
  }

The relationships in the models:

Block.php

public function blocks()
  {
    return $this->hasMany('App\Block')->orderby('section');
  }

Page.php

public function page()
  {
    return $this->belongsTo('App\Page', 'parent_id');
  }

CodePudding user response:

I don't think this is a proper solution, You may avoid using the eloquent logic inside a migration file

You may use the seed (If you are attending to run this multiple times regarding tests/dummy data...etc.), besides the code seems a bit confusing.

What I'll do, instead, is the following:

  • create new seed file:
php artisan make:seed MigrationSeed

Inside the MigrationSeed do the following:

    public function run()
    {
        $columns = collect(['page_id', 'post_id', 'event_id', 'global_id']);

        $columns->each(function ($column) {
            $this->updateColumn($column);
        });
    }

    private function updateColumn(string $column)
    {
        $blocks = Block::whereNotNull($column)->get();

        $blocks->each(function (Block $block) use ($column) {
            $block->update([
                'parent_id' => $block->{$column},
                'parent_type' => $column,
            ]);
        });
    }

Here, you'll create a list of columns you want to change, then make a separate method to get the NOT NULL column, and returns the data, then it will update the parent_id & parent_type.

It seems a bit complicated, but cleaner/extendable way to use in the present and the future.

Finally, run:

php artisan db:seed
  • Related