Skip to content

Instantly share code, notes, and snippets.

@rodrigopedra
Last active April 29, 2020 21:58
Show Gist options
  • Save rodrigopedra/99c46fb698e769d93a579608a4d962fa to your computer and use it in GitHub Desktop.
Save rodrigopedra/99c46fb698e769d93a579608a4d962fa to your computer and use it in GitHub Desktop.
Laravel Tenant Migration commands

The migrations that should run on the tenant connection will be stored on a tenants directory inside the migrations directory.

The custom commands are:

php artisan app:migrate {tenant-id}
php artisan app:rollback {tenant-id}

I forwarded just some of the parameters to the rollback command.

There might be some improvements and edges to improve. I started the project where I use this code in 2016, but it is just an example on my approach.

<?php
namespace App\Console\Commands;
use App\Models\Tenants\Tenant;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Database\Console\Migrations\MigrateCommand;
class MigrateTenant extends MigrateCommand
{
use ConfirmableTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:migrate {tenant : The tenant id to migrate.}
{--force : Force the operation to run when in production.}
{--path= : The path of migrations files to be executed.}
{--pretend : Dump the SQL queries that would be run.}
{--seed : Indicates if the seed task should be re-run.}
{--step : Force the migrations to be run so they can be rolled back individually.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Run the database migrations for a tenant';
public function __construct()
{
parent::__construct( app( 'migrator' ) );
}
/**
* Prepare the migration database for running.
*
* @return void
*/
protected function prepareDatabase()
{
$connection = $this->prepareConnection();
$this->migrator->setConnection( $connection->getName() );
if (!$this->migrator->repositoryExists()) {
$this->call( 'migrate:install', [ '--database' => 'tenant' ] );
}
}
protected function getMigrationPaths()
{
return [ parent::getMigrationPath() . DIRECTORY_SEPARATOR . 'tenants' ];
}
protected function prepareConnection()
{
$tenantId = $this->argument( 'tenant' );
$databaseName = Tenant::getTenantDatabaseName( $tenantId );
$connection = ( new Tenant )->getConnection();
$connection->statement( 'CREATE SCHEMA IF NOT EXISTS ' . $databaseName );
Tenant::setTenantDatabaseName( $tenantId );
return tap( DB::connection( 'tenant' ), function ( $connection ) {
$connection->reconnect();
} );
}
}
<?php
namespace App\Console\Commands;
use App\Models\Tenants\Tenant;
use Illuminate\Support\Facades\DB;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Database\Console\Migrations\RollbackCommand;
class RollbackTenant extends RollbackCommand
{
use ConfirmableTrait;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:rollback {tenant : The tenant id to migrate.}
{--force : Force the operation to run when in production.}
{--pretend : Dump the SQL queries that would be run.}
{--step : Force the migrations to be run so they can be rolled back individually.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rollback the last database migration for a tenant';
public function __construct()
{
parent::__construct( app( 'migrator' ) );
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
if (!$this->confirmToProceed()) {
return;
}
$connection = $this->prepareConnection();
$this->migrator->setConnection( $connection->getName() );
$this->migrator->setOutput( $this->output )->rollback(
$this->getMigrationPaths(), [
'pretend' => $this->option( 'pretend' ),
'step' => (int) $this->option( 'step' ),
]
);
}
protected function getMigrationPaths()
{
return [ parent::getMigrationPath() . DIRECTORY_SEPARATOR . 'tenants' ];
}
protected function prepareConnection()
{
$tenantId = $this->argument( 'tenant' );
Tenant::setTenantDatabaseName( $tenantId );
return tap( DB::connection( 'tenant' ), function ( $connection ) {
$connection->reconnect();
} );
}
}
<?php
namespace App\Models\Tenants;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
class Tenant extends Model
{
protected $connection = 'system';
public static function getTenantDatabaseName( $tenantId )
{
return sprintf( 'tenant_%04d', $tenantId );
}
public static function setTenantDatabaseName( $tenantId )
{
$databaseName = static::getTenantDatabaseName( $tenantId );
if ($databaseName === Config::get( 'database.connections.tenant.database' )) {
return $databaseName;
}
DB::purge( 'tenant' );
Config::set( 'database.connections.tenant.database', $databaseName );
DB::connection( 'tenant' )->reconnect();
return $databaseName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment