In episodes 1 and 2 of the new Foreign Devs Podcast, Victor and I discuss a project I’m in the middle of doing a major refactor on, and how I’m using migrations beyond just database changes. As I promised our listeners, I’m sharing a quick example of how I’m deprecating classes and introducing newly refactored ones without breaking functionality or relationships in Laravel.

Where this shines is that I’m able to utilize Laravel methods and relationships until I’m ready to introduce the new models (and new relationships).

The following migration is the second to last from a set of twelve in this refactor, it utilizes the Filesystem to move outgoing classes into a deprecated folder, and brings in new classes from a refactor folder.

Example Migration

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Filesystem\Filesystem;

class RefactorToInventoryRecordsUpdateModels extends Migration
{
    /**
     * Initiate class
     * 
     * @return void
     */
    public function __construct()
    {
        $this->basePath = app_path() . '/';
        $this->fileSystem = new Filesystem;

        $this->alterations = [
            'Sale' => 'deprecated/Sale',
            'Task' => 'deprecated/Task',
            'InventoryItem' => 'deprecated/InventoryItem',

            'refactor/Sale' => 'Sale',
            'refactor/Task' => 'Task',
            'refactor/InventoryRecord' => 'InventoryRecord',
        ];
    }

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        if (app()->runningUnitTests()) {
            return false;
        }

        foreach($this->alterations as $path => $target) {
            $this->move($path, $target);
        }
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        if (app()->runningUnitTests()) {
            return false;
        }
        
        foreach(array_reverse($this->alterations) as $target => $path) {
            $this->move($path, $target);
        }
    }

    /**
     * Moves the file from $path to $target
     *
     * @param string $path
     * @param string $target
     * @return boolean
     */
    protected function move(string $path, string $target)
    {
        return $this->fileSystem->move($this->basePath . $path . '.php', $this->basePath . $target . '.php');
    }
}

Relationships remain intact

Because we are replacing complete classes (and can undo it with a simple php artisan migrate:rollback command), we have the ability to perform any Eloquent calls on prior to running the above migration. For example, an eariler migration might update the Polymorphic class that Pictures belong to:

<?php

use App\InventoryItem;
use Illuminate\Database\Migrations\Migration;

class RefactorToInventoryRecordsUpdatePictures extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        InventoryItem::chunk(1000, function($inventoryItem) {
            $inventoryItem->pictures->each->update(['picturable_type' => 'App\InventoryRecord']);
        });
    }

While the project I’m working on is more complex than these examples, hopefully they provide some inspiration of how useful migrations can be for any one-time-use operations.

🎙️ Foreign Devs Podcast

🔥 New to Laravel or want to take your skills to the next level?

Be sure to check out Coder’s Tape, as Victor breaks down complex concepts into easy to understand material

Want to brainstorm some ideas or need more examples?

Send me a message on Twitter: @jani_gyllenberg