From 743369cde7f740e5675a823fd51757d1aac2d2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 2 Dec 2019 20:36:44 +0100 Subject: [PATCH 1/3] wip --- assets/config.php | 7 +- src/DatabaseManager.php | 23 ++-- .../PostgreSQLSchemaManager.php | 2 +- tests/DatabaseSchemaManagerTest.php | 102 +++++++++++++++--- tests/TenantDatabaseManagerTest.php | 2 +- tests/TestCase.php | 3 +- 6 files changed, 107 insertions(+), 32 deletions(-) diff --git a/assets/config.php b/assets/config.php index f590b29e..94c471fa 100644 --- a/assets/config.php +++ b/assets/config.php @@ -59,8 +59,8 @@ return [ // Tenant database managers handle the creation & deletion of tenant databases. '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\PostgreSQLDatabaseManager::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..b65ed421 100644 --- a/src/DatabaseManager.php +++ b/src/DatabaseManager.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Stancl\Tenancy; use Closure; +use Exception; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Database\DatabaseManager as BaseDatabaseManager; use Illuminate\Foundation\Application; @@ -100,12 +101,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 +229,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']; @@ -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 - * @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..0d36aea7 100644 --- a/tests/DatabaseSchemaManagerTest.php +++ b/tests/DatabaseSchemaManagerTest.php @@ -4,41 +4,38 @@ 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 */ @@ -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..6e072e83 100644 --- a/tests/TenantDatabaseManagerTest.php +++ b/tests/TenantDatabaseManagerTest.php @@ -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(); From a4fd296d03cc1457fd44ba18c62f8c226e36cebf Mon Sep 17 00:00:00 2001 From: stancl Date: Mon, 2 Dec 2019 19:37:03 +0000 Subject: [PATCH 2/3] Apply fixes from StyleCI --- src/DatabaseManager.php | 7 +++---- tests/DatabaseSchemaManagerTest.php | 2 +- tests/TenantDatabaseManagerTest.php | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/DatabaseManager.php b/src/DatabaseManager.php index b65ed421..0dd6c317 100644 --- a/src/DatabaseManager.php +++ b/src/DatabaseManager.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Stancl\Tenancy; use Closure; -use Exception; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Database\DatabaseManager as BaseDatabaseManager; use Illuminate\Foundation\Application; @@ -239,10 +238,10 @@ class DatabaseManager return $this->app[$databaseManagers[$driver]]; } - + /** * What key on the connection config should be used to separate tenants. - * + * * @param string $connectionName * @return string */ @@ -250,7 +249,7 @@ class DatabaseManager { if ($this->getDriver($this->getBaseConnection($connectionName)) === 'pgsql' && $this->app['config']['tenancy.separate_by'] === 'schema') { - return 'schema'; + return 'schema'; } return 'database'; diff --git a/tests/DatabaseSchemaManagerTest.php b/tests/DatabaseSchemaManagerTest.php index 0d36aea7..65c011c0 100644 --- a/tests/DatabaseSchemaManagerTest.php +++ b/tests/DatabaseSchemaManagerTest.php @@ -42,7 +42,7 @@ class DatabaseSchemaManagerTest extends TestCase 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', diff --git a/tests/TenantDatabaseManagerTest.php b/tests/TenantDatabaseManagerTest.php index 6e072e83..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], - ['pgsql', PostgreSQLSchemaManager::class] + ['pgsql', PostgreSQLSchemaManager::class], ]; } From cf2f98ad080bb6e0e7949bd7e84d70fefda24c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Mon, 2 Dec 2019 20:39:03 +0100 Subject: [PATCH 3/3] revert to db as default for pgsql --- assets/config.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/config.php b/assets/config.php index 94c471fa..42798b19 100644 --- a/assets/config.php +++ b/assets/config.php @@ -59,8 +59,8 @@ return [ // Tenant database managers handle the creation & deletion of tenant databases. 'sqlite' => Stancl\Tenancy\TenantDatabaseManagers\SQLiteDatabaseManager::class, 'mysql' => Stancl\Tenancy\TenantDatabaseManagers\MySQLDatabaseManager::class, - // 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class, - 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database + 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::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