diff --git a/assets/config.php b/assets/config.php index f590b29e..42798b19 100644 --- a/assets/config.php +++ b/assets/config.php @@ -60,7 +60,7 @@ return [ 'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class, 'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::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' => [ // Connections used by TenantDatabaseManagers. This tells, for example, the @@ -68,9 +68,8 @@ return [ 'sqlite' => 'sqlite', 'mysql' => 'mysql', 'pgsql' => 'pgsql', - 'schema' => 'pgsql' ], - 'using_schema_connection' => false, // Only work with pgsql connection + 'separate_by' => 'database', // database or schema (only supported by pgsql) 'bootstrappers' => [ // Tenancy bootstrappers are executed when tenancy is initialized. // Their responsibility is making Laravel features tenant-aware. diff --git a/src/DatabaseManager.php b/src/DatabaseManager.php index cd52e19a..0dd6c317 100644 --- a/src/DatabaseManager.php +++ b/src/DatabaseManager.php @@ -100,12 +100,9 @@ class DatabaseManager // Change database name. $databaseName = $this->getDriver($connectionName) === 'sqlite' ? database_path($databaseName) : $databaseName; + $separateBy = $this->separateBy($connectionName); - if ($this->isUsingSchema($connectionName)) { - $this->app['config']["database.connections.$connectionName.schema"] = $databaseName; - } else { - $this->app['config']["database.connections.$connectionName.database"] = $databaseName; - } + $this->app['config']["database.connections.$connectionName.$separateBy"] = $databaseName; } /** @@ -231,7 +228,7 @@ class DatabaseManager */ 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']; @@ -241,15 +238,20 @@ class DatabaseManager return $this->app[$databaseManagers[$driver]]; } - + /** - * Check if using schema connection - * + * What key on the connection config should be used to separate tenants. + * * @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'; } } diff --git a/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php b/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php index c255a081..fe753304 100644 --- a/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php +++ b/src/TenantDatabaseManagers/PostgreSQLSchemaManager.php @@ -15,7 +15,7 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager 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 diff --git a/tests/DatabaseSchemaManagerTest.php b/tests/DatabaseSchemaManagerTest.php index 5581232f..65c011c0 100644 --- a/tests/DatabaseSchemaManagerTest.php +++ b/tests/DatabaseSchemaManagerTest.php @@ -4,48 +4,45 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests; -use Stancl\Tenancy\DatabaseManager; +use Stancl\Tenancy\Tenant; class DatabaseSchemaManagerTest extends TestCase { public $autoInitTenancy = false; - /** - * Define environment setup. - * - * @param \Illuminate\Foundation\Application $app - * @return void - */ protected function getEnvironmentSetUp($app) { parent::getEnvironmentSetUp($app); - $app['config']->set('database.default', 'pgsql'); - $app['config']->set('tenancy.database.based_on', null); - $app['config']->set('tenancy.database.suffix', ''); - $app['config']->set('tenancy.using_schema_connection', true); + $app['config']->set([ + 'database.default' => 'pgsql', + 'database.connections.pgsql.database' => 'main', + 'database.connections.pgsql.schema' => 'public', + 'tenancy.database.based_on' => null, + 'tenancy.database.suffix' => '', + 'tenancy.separate_by' => 'schema', + ]); } /** @test */ public function reconnect_method_works() { $old_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName(); - + tenancy()->init('test.localhost'); app(\Stancl\Tenancy\DatabaseManager::class)->reconnect(); - + $new_connection_name = app(\Illuminate\Database\DatabaseManager::class)->connection()->getName(); - // $this->assertSame($old_connection_name, $new_connection_name); - $this->assertNotEquals('tenant', $new_connection_name); + $this->assertSame($old_connection_name, $new_connection_name); } /** @test */ public function the_default_db_is_used_when_based_on_is_null() { config(['database.default' => 'pgsql']); - + $this->assertSame('pgsql', config('database.default')); config([ 'database.connections.pgsql.foo' => 'bar', @@ -66,4 +63,79 @@ class DatabaseSchemaManagerTest extends TestCase $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); + } } diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index eea7bc48..89c0bbd7 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -10,8 +10,8 @@ use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter; use Stancl\Tenancy\Tenant; use Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager; -use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager; use Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager; +use Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager; class TenantDatabaseManagerTest extends TestCase { @@ -79,7 +79,7 @@ class TenantDatabaseManagerTest extends TestCase ['mysql', MySQLDatabaseManager::class], ['sqlite', SQLiteDatabaseManager::class], ['pgsql', PostgreSQLDatabaseManager::class], - ['schema', PostgreSQLSchemaManager::class] + ['pgsql', PostgreSQLSchemaManager::class], ]; } diff --git a/tests/TestCase.php b/tests/TestCase.php index 8df82561..2621d0a9 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -24,11 +24,12 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase Redis::connection('tenancy')->flushdb(); Redis::connection('cache')->flushdb(); + $originalConnection = config('database.default'); $this->loadMigrationsFrom([ '--path' => realpath(__DIR__ . '/../assets/migrations'), '--database' => 'central', ]); - config(['database.default' => 'sqlite']); // fix issue caused by loadMigrationsFrom + config(['database.default' => $originalConnection]); // fix issue caused by loadMigrationsFrom if ($this->autoCreateTenant) { $this->createTenant();