IPERAMUNA.COM
Mastering Dynamic Database Table Prefixes in Laravel
Article February 2, 2026

Mastering Dynamic Database Table Prefixes in Laravel

Implement configurable runtime database prefixes in Laravel by overriding 'make:migration' with custom stubs and a dedicated CLI flag.

In many real-world Laravel applications (especially SaaS, multi-tenant systems, or reusable packages), you may want to achieve the following:

  • All tables to be prefixed dynamically.
  • The prefix to come from a configuration file.
  • The same package to be installed in multiple apps with different prefixes.
  • Continue using the standard Artisan command: php artisan make:migration.

The Problem

By default, Laravel migrations are static. When you run the generation command, it creates a file with hardcoded table names:

Schema::create('users', ...)

The Motivation: Drop-in Installation

When you ship a professional package, you want it to be a true drop-in install. You want to avoid forcing your users to manually rename tables or edit vendor migrations, which inevitably leads to broken upgrades. The goal is a package that adapts itself perfectly based on configuration only.


The Goal

We want to implement a system where:

  1. Add a CLI flag like --pkg.
  2. When present:
    • Laravel uses custom migration stubs.
    • Those stubs generate migrations using config('pkg_config.db_prefix').
  3. No breaking changes: Standard Laravel behavior remains untouched.
  4. No core rewrites: Avoid overriding base Artisan commands.

The Strategy

Our approach involves:

  1. Adding a custom CLI argument (--pkg).
  2. Stripping the argument before Symfony parses it in artisan.
  3. Setting a runtime flag.
  4. Overriding Laravel’s MigrationCreator in a Service Provider.
  5. Pointing the creator to custom stub files.

This works seamlessly in Laravel 11/12 and follows framework internals correctly.


Step 1: Create Custom Migration Stubs

First, publish all Laravel stubs to your project’s base path:

php artisan stub:publish

This creates a /stubs folder. For our use case, we will modify migration.create.stub.

The Migration Create Stub

<?php

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

return new class extends Migration
{
    /**
     * Resolve the table name with a runtime prefix.
     */
    protected function table(string $name): string
    {
        $prefix = config('pkg_config.db_prefix', '');
        return $prefix ? "{$prefix}_{$name}" : $name;
    }

    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create($this->table('{{ table }}'), function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists($this->table('{{ table }}'));
    }
};

You can also update migration.update.stub and migration.stub. Laravel will select the appropriate one automatically.


Step 2: Add CLI Flag Handling in artisan

Open the root artisan file and add the following logic before the application boots:

if (PHP_SAPI === 'cli') {
    $argv = $_SERVER['argv'] ?? [];

    $key = array_search('--pkg', $argv, true);

    if ($key !== false) {
        // Remove the flag so Symfony Console doesn't error on unknown flags
        unset($argv[$key]);
        $_SERVER['argv'] = array_values($argv);

        // Pass flag into Laravel runtime environment
        $_SERVER['PKG_MIGRATION_STUBS'] = '1';
    }
}

Automating the Injection

You can use the following bash script to inject this logic automatically. It will ask for the flag name and update your artisan file safely.

# See setup-artisan-flag.sh in this folder

Step 3: Override MigrationCreator in Service Provider

In your AppServiceProvider or a dedicated package provider:

use Illuminate\Database\Migrations\MigrationCreator;

public function register(): void
{
    if (! $this->app->runningInConsole()) {
        return;
    }

    if (($_SERVER['PKG_MIGRATION_STUBS'] ?? null) !== '1') {
        return;
    }

    $this->app->extend(MigrationCreator::class, function ($creator, $app) {
        return new MigrationCreator(
            $app['files'],
            base_path('stubs') // Use your custom stubs directory
        );
    });
}

Step 4: Configuration

Create a configuration file:

Config: pkg_config.php

<?php

return [
    'db_prefix' => env('PKG_DB_PREFIX', 'tenant'),
];

Now your migrations are fully runtime-configurable.


Usage

Default Laravel Behavior (Unchanged)

php artisan make:migration create_users_table --create=users

Generates:

Schema::create('users', ...)

Package-Prefixed Behavior

php artisan make:migration create_users_table --create=users --pkg

Generates:

Schema::create($this->table('users'), ...)

Why This Approach is Powerful

Feature Benefit
CLI Flag Purely opt-in behavior.
Custom Stubs Clean implementation without core hacks.
Runtime Config Perfect for multi-tenant and reusable packages.
Future-Proof Designed for Laravel 11/12.

Important Warning ⚠️

Using config() inside migrations makes the output dependent on your runtime configuration. This is safe only if:

  1. The prefix is set once at installation.
  2. It never changes thereafter.

Otherwise, different environments may end up with mismatching schemas. This pattern is ideal for reusable packages, not for dynamic live production schemas.


Final Thoughts

By leveraging Laravel’s internal service container and customizing stubs, we’ve built a migration system that is both powerful and developer-friendly. This approach doesn't just solve the prefixing problem; it demonstrates a core philosophy of Laravel: the framework is built to be extended, not hacked.

You now have a solution that:

  • Respects Framework Defaults: Keeps standard migrations untouched.
  • Empowers Package Users: Provides a seamless, configuration-first installation.
  • Scales with Your Project: Handles complex multi-tenant or SaaS architectures with ease.

Whether you're building the next great open-source package or a complex internal enterprise system, this pattern ensures your database layer remains flexible, maintainable, and—most importantly—future-proof. 💎

Just remember:

php artisan make:migration --pkg

It’s not just customization; it’s building the Laravel way.

Shell Scripts

Artisan Flag