Hopefully, I can explain this well.
I have a Laravel application that has been in production for a minute. So, I have a bunch of migration files with a lot of changes. I would like to consolidate these migration files without losing the database.
The way I think this would work:
Part of why I would like to do this is because I would like to make some of the service providers public with the cleanest migration set possible.
The difficult version might be to:
Just hoping there's an easier way than that.
Edit (from comments): I have a production database that has about 50+ migration files - some minor changes, some large changes. If I consolidated, the number of migrations needed would be about 12 or so. I would like to consolidate the migration files, but still be able to perform migrate:rollback
on production - not that I would.
After a couple of over-engineered and overly clever solution attempts, I think the following is a workable solution to the problem.
tl;dr:
migrations
table. The first bookend renames the affected tables. The second bookend copies the data from the renamed tables to the fresh tables, then deletes the renamed tables.
Note: You can do whatever you like inside the bookends, this is just a minimum.
So, let's say you something like the following for migrations:
We would create another migration:
We would create another migration based on the knowledge we have now:
We would create the last bookend, where data migration will occur:
The first four migrations will not be run because they already have been.
/** 2017_09_05_000004_pre_refresh.php */
class PreRefresh extends Migration
{
public function up()
{
$prefix = 'zz_';
$tablesToRename = [
'foos',
'bars'
];
foreach($tablesToRename as $table) {
Schema::rename($table, $prefix . $table);
}
}
}
No need for a down, because this is a one shot deal. This will run first, which should result in all the tables listed in the array being renamed. Then the consolidated (optimized) migration(s) will run.
/** 2017_09_05_000006_post_refresh.php */
class PostRefresh extends Migration
{
public function up()
{
// Do what you need to do.
// If you cannot use your models, just use DB::table() commands.
$foos = DB::table('zz_foos')->get();
foreach ($foos as $foo) {
DB::table('foo')->insert([
'id' => $foo->id,
'created_at' => $foo->created_at,
'updated_at' => $foo->updated_at
]);
}
$bars = DB::table('zz_bars')->get();
foreach ($bars as $bar) {
DB::table('bar')->insert([
'id' => $bar->id,
'created_at' => $bar->created_at,
'updated_at' => $bar->updated_at,
'foo_id' => $bar->foo_id
]);
}
// Tear down.
$prefix = 'zz_';
$tablesToRename = [
'foo',
'bar'
];
foreach ($tablesToRename as $table) {
DB::statement('SET FOREIGN_KEY_CHECKS=0');
Schema::dropIfExists($prefix . $table);
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
}
}
After running this, you can delete all your migrations from the pre_refresh
and prior. As well as the post_refresh
. Then you can head into the migrations
table and delete the entries for those migrations.
Deleting the entries isn't entirely necessary, but if you migrate:rollback
you will get error messages stating that the migration can't be found.
Note: When I actually do this in production, not just my local (over and over again), and if there is not a better answer, then I will accept this.
If you are breaking your application into service providers with discreet migrations, then you can comment out the service provider in /config/app
when you run the migrations. This way you create a batch for the now baselined service. So, let's say you have the following migrations where each letter represents a migration, and each duplicate letter represents the same service:
After consolidating service A:
After consolidating B:
After consolidating C:
update
54 migrations down to 27 so far. I even pulled out some Schema changes from large up()
and down()
methods and make them separate migrations. The nice side-effect here is the batches. I migrated starting with the base tables upon which everything else is supported; therefore, rolling back is more service by service.