14

Laravel ships with database migrations for managing CRUD operations regarding the structure of a database, but what is the appropriate/recommended/standardized way to handle migration of actual data?

My question is, should the data migration take place directly inside the database migration file? Should it be a seeder? Should it be a job that is dispatched from within the database migration? Where should such logic go. Sometimes these data migrations can become incredibly complex depending on what the database migration does, and in the spirit of maximizing readability and keeping responsibilities separate, I feel like the logic belongs somewhere else.

This question, I suppose, is more attributable to OOP programming structure and practice as a whole, rather than laravel specific, but Laravel is the framework I'm working in right now so framing my question in that regard.

mindfullsilence
  • 344
  • 9
  • 23

3 Answers3

13

I've done this several times, and I do it right there in the migration up() and down() functions unless we're talking about millions of records. I agree with you, it feels like there should be a clearly defined function in the migration for this. We want the data changed before another migration on the table is triggered, so I feel it needs to be done right away.

Using your example, this is what a simple migration would look like for splitting the name into a first_name and last_name in the up() function:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

class Test extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('last_name')->after('name');
            $table->string('first_name')->after('name');
        });

        DB::statement("UPDATE users SET first_name = SUBSTRING_INDEX(name, ' ', 1), last_name = SUBSTRING(name from instr(name, ' ') + 1)");

        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('name');
        });
    }

...

If you have complex data changes, take a look at the $table->temporary(); option to create temporary tables to do data manipulation with SQL, and/or make command scripts which are called within the migration using the Artisan::call().

$table->temporary(): https://laravel.com/docs/8.x/migrations#database-connection-table-options Artisan::call(): https://laravel.com/docs/8.x/artisan#programmatically-executing-commands

Dharman
  • 30,962
  • 25
  • 85
  • 135
jon__o
  • 1,509
  • 13
  • 14
2

I prefer to separate data and structure migrations. I think that migration files should include only schema related queries.

Conditionally migration could contain data changes if:

  • Data is dependant on the time of deployment/migration (Can't really think of a case, but I am sure there are some :)).
  • We are making a schema change that directly affects the data. For example: changing the type of column or creating a new key that has to be seeded before future migrations take place.

Additional reasons why I prefer to have data in seeder files:

  • Running migrations on productions always carries certain risks. You can lower the risks of losing your data by testing the deployment process and using some fancy CD processes, but the risk is always present.

  • Static data that you think will never change, will change. For example, you start a new project in 2010 and the project's database contains table 'countries', which contains a list of countries and their properties. But after 2011 you get a new country: South Sudan. Will you create new migration or just update the seeder?

Sigismund
  • 1,053
  • 9
  • 21
0

Adding to the answers by @jon__o and you can find more information here. Also, I will recommend that you refer to this link where they used temporary tables based on hashed_id where temporary tables are basically identical to the normal tables in the database. It has many features that are useful for migrations.

Schema::create('temp_mappings', function (Blueprint $table) {
        $table->temporary(); // thanks, Laravel
        $table->integer('id')->primary();
        $table->string('hash_id');
    });
I_Al-thamary
  • 3,385
  • 2
  • 24
  • 37