mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 10:14:04 +00:00
wip
This commit is contained in:
parent
2ff3dd4283
commit
743369cde7
6 changed files with 107 additions and 32 deletions
|
|
@ -59,8 +59,8 @@ return [
|
||||||
// Tenant database managers handle the creation & deletion of tenant databases.
|
// Tenant database managers handle the creation & deletion of tenant databases.
|
||||||
'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
|
'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class,
|
||||||
'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
|
'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class,
|
||||||
'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
|
// 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
|
||||||
'schema' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class
|
'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
|
||||||
],
|
],
|
||||||
'database_manager_connections' => [
|
'database_manager_connections' => [
|
||||||
// Connections used by TenantDatabaseManagers. This tells, for example, the
|
// Connections used by TenantDatabaseManagers. This tells, for example, the
|
||||||
|
|
@ -68,9 +68,8 @@ return [
|
||||||
'sqlite' => 'sqlite',
|
'sqlite' => 'sqlite',
|
||||||
'mysql' => 'mysql',
|
'mysql' => 'mysql',
|
||||||
'pgsql' => 'pgsql',
|
'pgsql' => 'pgsql',
|
||||||
'schema' => 'pgsql'
|
|
||||||
],
|
],
|
||||||
'using_schema_connection' => false, // Only work with pgsql connection
|
'separate_by' => 'database', // database or schema (only supported by pgsql)
|
||||||
'bootstrappers' => [
|
'bootstrappers' => [
|
||||||
// Tenancy bootstrappers are executed when tenancy is initialized.
|
// Tenancy bootstrappers are executed when tenancy is initialized.
|
||||||
// Their responsibility is making Laravel features tenant-aware.
|
// Their responsibility is making Laravel features tenant-aware.
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Stancl\Tenancy;
|
namespace Stancl\Tenancy;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
|
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
|
|
@ -100,12 +101,9 @@ class DatabaseManager
|
||||||
|
|
||||||
// Change database name.
|
// Change database name.
|
||||||
$databaseName = $this->getDriver($connectionName) === 'sqlite' ? database_path($databaseName) : $databaseName;
|
$databaseName = $this->getDriver($connectionName) === 'sqlite' ? database_path($databaseName) : $databaseName;
|
||||||
|
$separateBy = $this->separateBy($connectionName);
|
||||||
|
|
||||||
if ($this->isUsingSchema($connectionName)) {
|
$this->app['config']["database.connections.$connectionName.$separateBy"] = $databaseName;
|
||||||
$this->app['config']["database.connections.$connectionName.schema"] = $databaseName;
|
|
||||||
} else {
|
|
||||||
$this->app['config']["database.connections.$connectionName.database"] = $databaseName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -231,7 +229,7 @@ class DatabaseManager
|
||||||
*/
|
*/
|
||||||
public function getTenantDatabaseManager(Tenant $tenant): TenantDatabaseManager
|
public function getTenantDatabaseManager(Tenant $tenant): TenantDatabaseManager
|
||||||
{
|
{
|
||||||
$driver = $this->isUsingSchema($tenant->getConnectionName()) ? 'schema' : $this->getDriver($this->getBaseConnection($tenant->getConnectionName()));
|
$driver = $this->getDriver($this->getBaseConnection($tenant->getConnectionName()));
|
||||||
|
|
||||||
$databaseManagers = $this->app['config']['tenancy.database_managers'];
|
$databaseManagers = $this->app['config']['tenancy.database_managers'];
|
||||||
|
|
||||||
|
|
@ -243,13 +241,18 @@ class DatabaseManager
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if using schema connection
|
* What key on the connection config should be used to separate tenants.
|
||||||
*
|
*
|
||||||
* @param string $connectionName
|
* @param string $connectionName
|
||||||
* @return bool
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function isUsingSchema(string $connectionName): bool
|
public function separateBy(string $connectionName): string
|
||||||
{
|
{
|
||||||
return $this->getDriver($this->getBaseConnection($connectionName)) === 'pgsql' && $this->app['config']['tenancy.using_schema_connection'];
|
if ($this->getDriver($this->getBaseConnection($connectionName)) === 'pgsql'
|
||||||
|
&& $this->app['config']['tenancy.separate_by'] === 'schema') {
|
||||||
|
return 'schema';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'database';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager
|
||||||
|
|
||||||
public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager)
|
public function __construct(Repository $config, IlluminateDatabaseManager $databaseManager)
|
||||||
{
|
{
|
||||||
$this->database = $databaseManager->connection($config['tenancy.database_manager_connections.schema']);
|
$this->database = $databaseManager->connection($config['tenancy.database_manager_connections.pgsql']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createDatabase(string $name): bool
|
public function createDatabase(string $name): bool
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,24 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Stancl\Tenancy\Tests;
|
namespace Stancl\Tenancy\Tests;
|
||||||
|
|
||||||
use Stancl\Tenancy\DatabaseManager;
|
use Stancl\Tenancy\Tenant;
|
||||||
|
|
||||||
class DatabaseSchemaManagerTest extends TestCase
|
class DatabaseSchemaManagerTest extends TestCase
|
||||||
{
|
{
|
||||||
public $autoInitTenancy = false;
|
public $autoInitTenancy = false;
|
||||||
|
|
||||||
/**
|
|
||||||
* Define environment setup.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Foundation\Application $app
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function getEnvironmentSetUp($app)
|
protected function getEnvironmentSetUp($app)
|
||||||
{
|
{
|
||||||
parent::getEnvironmentSetUp($app);
|
parent::getEnvironmentSetUp($app);
|
||||||
|
|
||||||
$app['config']->set('database.default', 'pgsql');
|
$app['config']->set([
|
||||||
$app['config']->set('tenancy.database.based_on', null);
|
'database.default' => 'pgsql',
|
||||||
$app['config']->set('tenancy.database.suffix', '');
|
'database.connections.pgsql.database' => 'main',
|
||||||
$app['config']->set('tenancy.using_schema_connection', true);
|
'database.connections.pgsql.schema' => 'public',
|
||||||
|
'tenancy.database.based_on' => null,
|
||||||
|
'tenancy.database.suffix' => '',
|
||||||
|
'tenancy.separate_by' => 'schema',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
|
|
@ -37,8 +35,7 @@ class DatabaseSchemaManagerTest extends TestCase
|
||||||
|
|
||||||
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
|
$new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName();
|
||||||
|
|
||||||
// $this->assertSame($old_connection_name, $new_connection_name);
|
$this->assertSame($old_connection_name, $new_connection_name);
|
||||||
$this->assertNotEquals('tenant', $new_connection_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @test */
|
/** @test */
|
||||||
|
|
@ -66,4 +63,79 @@ class DatabaseSchemaManagerTest extends TestCase
|
||||||
|
|
||||||
$this->assertSame($tenant->getDatabaseName(), config('database.connections.' . config('database.default') . '.schema'));
|
$this->assertSame($tenant->getDatabaseName(), config('database.connections.' . config('database.default') . '.schema'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function databases_are_separated_using_schema_and_not_database()
|
||||||
|
{
|
||||||
|
tenancy()->create('foo.localhost');
|
||||||
|
tenancy()->init('foo.localhost');
|
||||||
|
$this->assertSame('tenant', config('database.default'));
|
||||||
|
$this->assertSame('main', config('database.connections.tenant.database'));
|
||||||
|
|
||||||
|
$schema1 = config('database.connections.' . config('database.default') . '.schema');
|
||||||
|
$database1 = config('database.connections.' . config('database.default') . '.database');
|
||||||
|
|
||||||
|
tenancy()->create('bar.localhost');
|
||||||
|
tenancy()->init('bar.localhost');
|
||||||
|
$this->assertSame('tenant', config('database.default'));
|
||||||
|
$this->assertSame('main', config('database.connections.tenant.database'));
|
||||||
|
|
||||||
|
$schema2 = config('database.connections.' . config('database.default') . '.schema');
|
||||||
|
$database2 = config('database.connections.' . config('database.default') . '.database');
|
||||||
|
|
||||||
|
$this->assertSame($database1, $database2);
|
||||||
|
$this->assertNotSame($schema1, $schema2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function databases_are_separated()
|
||||||
|
{
|
||||||
|
// copied from DataSeparationTest
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create('tenant1.localhost');
|
||||||
|
$tenant2 = Tenant::create('tenant2.localhost');
|
||||||
|
\Artisan::call('tenants:migrate', [
|
||||||
|
'--tenants' => [$tenant1['id'], $tenant2['id']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->init('tenant1.localhost');
|
||||||
|
User::create([
|
||||||
|
'name' => 'foo',
|
||||||
|
'email' => 'foo@bar.com',
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||||
|
'remember_token' => Str::random(10),
|
||||||
|
]);
|
||||||
|
$this->assertSame('foo', User::first()->name);
|
||||||
|
|
||||||
|
tenancy()->init('tenant2.localhost');
|
||||||
|
$this->assertSame(null, User::first());
|
||||||
|
|
||||||
|
User::create([
|
||||||
|
'name' => 'xyz',
|
||||||
|
'email' => 'xyz@bar.com',
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||||
|
'remember_token' => Str::random(10),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame('xyz', User::first()->name);
|
||||||
|
$this->assertSame('xyz@bar.com', User::first()->email);
|
||||||
|
|
||||||
|
tenancy()->init('tenant1.localhost');
|
||||||
|
$this->assertSame('foo', User::first()->name);
|
||||||
|
$this->assertSame('foo@bar.com', User::first()->email);
|
||||||
|
|
||||||
|
$tenant3 = Tenant::create('tenant3.localhost');
|
||||||
|
\Artisan::call('tenants:migrate', [
|
||||||
|
'--tenants' => [$tenant1['id'], $tenant3['id']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
tenancy()->init('tenant3.localhost');
|
||||||
|
$this->assertSame(null, User::first());
|
||||||
|
|
||||||
|
tenancy()->init('tenant1.localhost');
|
||||||
|
DB::table('users')->where('id', 1)->update(['name' => 'xxx']);
|
||||||
|
$this->assertSame('xxx', User::first()->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class TenantDatabaseManagerTest extends TestCase
|
||||||
['mysql', MySQLDatabaseManager::class],
|
['mysql', MySQLDatabaseManager::class],
|
||||||
['sqlite', SQLiteDatabaseManager::class],
|
['sqlite', SQLiteDatabaseManager::class],
|
||||||
['pgsql', PostgreSQLDatabaseManager::class],
|
['pgsql', PostgreSQLDatabaseManager::class],
|
||||||
['schema', PostgreSQLSchemaManager::class]
|
['pgsql', PostgreSQLSchemaManager::class]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,12 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
Redis::connection('tenancy')->flushdb();
|
Redis::connection('tenancy')->flushdb();
|
||||||
Redis::connection('cache')->flushdb();
|
Redis::connection('cache')->flushdb();
|
||||||
|
|
||||||
|
$originalConnection = config('database.default');
|
||||||
$this->loadMigrationsFrom([
|
$this->loadMigrationsFrom([
|
||||||
'--path' => realpath(__DIR__ . '/../assets/migrations'),
|
'--path' => realpath(__DIR__ . '/../assets/migrations'),
|
||||||
'--database' => 'central',
|
'--database' => 'central',
|
||||||
]);
|
]);
|
||||||
config(['database.default' => 'sqlite']); // fix issue caused by loadMigrationsFrom
|
config(['database.default' => $originalConnection]); // fix issue caused by loadMigrationsFrom
|
||||||
|
|
||||||
if ($this->autoCreateTenant) {
|
if ($this->autoCreateTenant) {
|
||||||
$this->createTenant();
|
$this->createTenant();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue