DEV Community

André Luiz Lunelli
André Luiz Lunelli

Posted on

Keep Code References Out of Your Migrations

TL;DR

Long story short, don't reference your business layer within migration files.

Migrations should be immutable snapshots of your database history, never dependent on evolving application code.

 Bad (breaks if enum class changes or is removed)

$table->enum('role', array_column(Can::cases(), 'value'));
Enter fullscreen mode Exit fullscreen mode
 Good (enum values explicitly defined, stable and independent)

$table->enum('role', ['access_admin', 'can_impersonate']);
Enter fullscreen mode Exit fullscreen mode

Read the Full Thing

Often, when we define enum values in a model, like a role property or a status for something, and we want to provide an input where the user can choose an option, it's a good practice to reference the enum class by using foreach logic to iterate over these values to display them for the user.

Something like:

// app/Enums/Role.php
enum Role: string
{
    case Admin = 'access_admin';
    case Moderator = 'can_impersonate';
    case User = 'regular_user';
}
Enter fullscreen mode Exit fullscreen mode
{{-- resources/views/users/form.blade.php --}}
<select name="role">
    @foreach(Role::cases() as $role)
        <option value="{{ $role->value }}">
            {{ str_replace('_', ' ', ucfirst($role->value)) }}
        </option>
    @endforeach
</select>
Enter fullscreen mode Exit fullscreen mode

That's a good practice, isn't it? Can you imagine if, instead of iterating over an enum to get those values, you chose to rewrite the same occurrences in the whole project (backend, frontend)? Not cool.

Why Migrations Are Different

Migrations aren't like your application code - they're historical records of how your database evolved over time. Once a migration runs in production, it should never need to change.

Migrations should be your database changelog

  • They document every schema change over time
  • With dynamic references, you lose that documentation
  • You can't look at migrations and understand how the schema evolved

Fragility

  • Today: Your Role enum has admin and user values
  • Migration runs: Creates column with those values, everything ok ✓
  • 6 months later: You rename Role enum to UserRole, or delete it entirely
  • New developer runs migrations: 💥 Fatal error - Role class not found

As a result

  • Forces us to edit old migrations
  • Creates extra work
  • Increases the risk of errors

Bonus

Also, I'd avoid using foreignIdFor() because it couples the migration directly to a model. If that model is deleted or renamed, the migration becomes invalid.

Well, that's all, folks.

Top comments (0)