1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-05 10:14:04 +00:00

Initial draft

This commit is contained in:
Samuel Štancl 2020-04-30 20:48:15 +02:00
parent 4ff82a950d
commit 5d7d208e2f
19 changed files with 376 additions and 186 deletions

View file

@ -100,7 +100,7 @@ return [
* The connection that will be used as a template for the dynamically created tenant connection.
* Set to null to use the default connection.
*/
'based_on' => null,
'template_connection' => null,
/**
* Tenant database names are created like this:
@ -108,8 +108,6 @@ return [
*/
'prefix' => 'tenant',
'suffix' => '',
'separate_by' => 'database', // database or schema (only supported by pgsql)
],
/**
@ -197,9 +195,14 @@ return [
'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLDatabaseManager::class,
/**
* Disable the pgsql manager above, enable the one below, and set the
* tenancy.database.separate_by config key to 'schema' if you would
* like to separate tenant DBs by schemas rather than databases.
* Use this database manager for MySQL to have a DB user created for each tenant database.
* You can customize the grants given to these users by changing the $grants property.
*/
// 'mysql' => Stancl\Tenancy\TenantDatabaseManagers\PermissionControlledMySQLDatabaseManager::class,
/**
* Disable the pgsql manager above, and enable the one below if you
* want to separate tenant DBs by schemas rather than databases.
*/
// 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database
],

View file

@ -0,0 +1,11 @@
<?php
namespace Stancl\Tenancy\Contracts;
use Stancl\Tenancy\DatabaseConfig;
interface ManagesDatabaseUsers
{
public function createUser(DatabaseConfig $databaseConfig): void;
public function deleteUser(DatabaseConfig $databaseConfig): void;
}

View file

@ -6,6 +6,13 @@ namespace Stancl\Tenancy\Contracts;
interface TenantDatabaseManager
{
/**
* Return the config key that separates databases (e.g. 'database' or 'schema').
*
* @return string
*/
public function getSeparator(): string;
/**
* Create a database.
*

167
src/DatabaseConfig.php Normal file
View file

@ -0,0 +1,167 @@
<?php
namespace Stancl\Tenancy;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
class DatabaseConfig
{
/** @var Tenant */
public $tenant;
/** @var callable */
public static $usernameGenerator;
/** @var callable */
public static $passwordGenerator;
/** @var callable */
public static $databaseNameGenerator;
public static function __constructStatic(): void
{
static::$usernameGenerator = static::$usernameGenerator ?? function (Tenant $tenant) {
return Str::random(16);
};
static::$passwordGenerator = static::$usernameGenerator ?? function (Tenant $tenant) {
return Hash::make(Str::random(32));
};
static::$databaseNameGenerator = static::$databaseNameGenerator ?? function (Tenant $tenant) {
return config('tenancy.database.prefix') . $tenant->id . config('tenancy.database.suffix');
};
}
public function __construct(Tenant $tenant)
{
static::__constructStatic();
$this->tenant = $tenant;
}
public static function generateDatabaseNameUsing(callable $databaseNameGenerator): void
{
static::$databaseNameGenerator = $databaseNameGenerator;
}
public static function generateUsernameUsing(callable $usernameGenerator): void
{
static::$usernameGenerator = $usernameGenerator;
}
public static function generatePasswordUsing(callable $passwordGenerator): void
{
static::$passwordGenerator = $passwordGenerator;
}
public function getName(): string
{
return $this->tenant->data['_tenancy_db_name'];
}
public function getUsername(): string
{
return $this->tenant->data['_tenancy_db_username'];
}
public function getPassword(): string
{
return $this->tenant->data['_tenancy_db_password'];
}
public function getTemplateDatabaseConnection(): string
{
return $this->tenant->data['_tenancy_db_connection'];
}
public function makeCredentials(): void
{
$this->tenant->data['_tenancy_db_name'] = $this->getName() ?? (static::$databaseNameGenerator)($this->tenant);
if ($this->manager() instanceof ManagesDatabaseUsers) {
$this->tenant->data['_tenancy_db_username'] = $this->getUsername() ?? (static::$usernameGenerator)($this->tenant);
$this->tenant->data['_tenancy_db_password'] = $this->getPassword() ?? (static::$passwordGenerator)($this->tenant);
}
$this->tenant->save();
}
/**
* Template used to construct the tenant connection. Also serves as
* the root connection on which the tenant database is created.
*/
public function getTemplateConnectionName()
{
$name = $this->tenant->getTemplateDatabaseConnection();
// If we're using e.g. 'tenant', the default, template connection
// and it doesn't exist, we'll go for the default DB template.
if (! array_key_exists($name, config('database.connections'))) {
$name = config('tenancy.database.template_connection') ?? DatabaseManager::$originalDefaultConnectionName;
};
return $name;
}
/**
* Tenant's own database connection config.
*/
public function connection(): array
{
$templateConnection = config("database.connections.{$this->getTemplateConnectionName()}");
return array_merge($templateConnection, $this->tenantConfig(), [
$this->manager()->getSeparator() => $this->tenant->database()->getName(),
]);
}
/**
* Additional config for the database connection, specific to this tenant.
*/
public function tenantConfig(): array
{
$dbConfig = array_filter(array_keys($this->tenant->data), function ($key) {
return Str::startsWith($key, '_tenancy_db_');
});
// Remove DB name because we set that separately
if (isset($dbConfig['_tenancy_db_name'])) {
unset($dbConfig['_tenancy_db_name']);
}
return array_reduce($dbConfig, function ($config, $key) {
return array_merge($config, [
Str::substr($key, 0, strlen('_tenancy_db_')) => $this->tenant[$key],
]);
}, []);
}
/**
* Get the TenantDatabaseManager for this tenant's connection.
*/
public function manager(): TenantDatabaseManager
{
$driver = config("database.connections.{$this->getTemplateConnectionName()}.driver");
$databaseManagers = config('tenancy.database_managers');
if (! array_key_exists($driver, $databaseManagers)) {
throw new DatabaseManagerNotRegisteredException($driver);
}
/** @var TenantDatabaseManager $databaseManager */
$databaseManager = app($databaseManagers[$driver]);
if ($databaseManager instanceof CanSetConnection) {
$databaseManager->setConnection($this->getTemplateConnectionName());
}
return $databaseManager;
}
}

View file

@ -8,18 +8,20 @@ use Closure;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
use Illuminate\Foundation\Application;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Exceptions\DatabaseManagerNotRegisteredException;
use Stancl\Tenancy\Exceptions\TenantDatabaseAlreadyExistsException;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseCreator;
use Stancl\Tenancy\Jobs\QueuedTenantDatabaseDeleter;
/**
* @internal Class is subject to breaking changes in minor and patch versions.
*/
class DatabaseManager
{
/** @var string */
public $originalDefaultConnectionName;
public static $originalDefaultConnectionName;
/** @var Application */
protected $app;
@ -34,14 +36,11 @@ class DatabaseManager
{
$this->app = $app;
$this->database = $database;
$this->originalDefaultConnectionName = $app['config']['database.default'];
static::$originalDefaultConnectionName = $app['config']['database.default'];
}
/**
* Set the TenantManager instance, used to dispatch tenancy events.
*
* @param TenantManager $tenantManager
* @return self
*/
public function withTenantManager(TenantManager $tenantManager): self
{
@ -52,21 +51,16 @@ class DatabaseManager
/**
* Connect to a tenant's database.
*
* @param Tenant $tenant
* @return void
*/
public function connect(Tenant $tenant)
{
$this->createTenantConnection($tenant->getDatabaseName(), $tenant->getConnectionName());
$this->createTenantConnection($tenant, $tenant->getConnectionName());
$this->setDefaultConnection($tenant->getConnectionName());
$this->switchConnection($tenant->getConnectionName());
}
/**
* Reconnect to the default non-tenant connection.
*
* @return void
*/
public function reconnect()
{
@ -78,9 +72,6 @@ class DatabaseManager
/**
* Change the default database connection config.
*
* @param string $connection
* @return void
*/
public function setDefaultConnection(string $connection)
{
@ -89,53 +80,14 @@ class DatabaseManager
/**
* Create the tenant database connection.
*
* @param string $databaseName
* @param string $connectionName
* @return void
*/
public function createTenantConnection($databaseName, $connectionName)
public function createTenantConnection(Tenant $tenant, $connectionName)
{
// Create the database connection.
$based_on = $this->getBaseConnection($connectionName);
$this->app['config']["database.connections.$connectionName"] = $this->app['config']['database.connections.' . $based_on];
// Change database name.
$databaseName = $this->getDriver($connectionName) === 'sqlite' ? database_path($databaseName) : $databaseName;
$separateBy = $this->separateBy($connectionName);
$this->app['config']["database.connections.$connectionName.$separateBy"] = $databaseName;
}
/**
* Get the name of the connection that $connectionName should be based on.
*
* @param string $connectionName
* @return string
*/
public function getBaseConnection(string $connectionName): string
{
return ($connectionName !== 'tenant' ? $connectionName : null) // 'tenant' is not a specific connection, it's the default
?? $this->app['config']['tenancy.database.based_on']
?? $this->originalDefaultConnectionName; // tenancy.database.based_on === null => use the default connection
}
/**
* Get the driver of a database connection.
*
* @param string $connectionName
* @return string|null
*/
public function getDriver(string $connectionName): ?string
{
return $this->app['config']["database.connections.$connectionName.driver"];
$this->app['config']["database.connections.$connectionName"] = $tenant->database->connection();
}
/**
* Switch the application's connection.
*
* @param string $connection
* @return void
*/
public function switchConnection(string $connection)
{
@ -146,16 +98,14 @@ class DatabaseManager
/**
* Check if a tenant can be created.
*
* @param Tenant $tenant
* @return void
*
* @throws TenantCannotBeCreatedException
* @throws DatabaseManagerNotRegisteredException
* @throws TenantDatabaseAlreadyExistsException
*/
public function ensureTenantCanBeCreated(Tenant $tenant): void
{
if ($this->getTenantDatabaseManager($tenant)->databaseExists($database = $tenant->getDatabaseName())) {
if ($tenant->database()->manager()->databaseExists($database = $tenant->database()->getName())) {
throw new TenantDatabaseAlreadyExistsException($database);
}
}
@ -170,50 +120,63 @@ class DatabaseManager
*/
public function createDatabase(Tenant $tenant, array $afterCreating = [])
{
$database = $tenant->getDatabaseName();
$manager = $this->getTenantDatabaseManager($tenant);
$tenant->database()->makeCredentials();
$afterCreating = array_merge(
$afterCreating,
$this->tenancy->event('database.creating', $database, $tenant)
$this->tenancy->event('database.creating', $tenant->database()->getName(), $tenant)
);
if ($this->app['config']['tenancy.queue_database_creation'] ?? false) {
$chain = [];
foreach ($afterCreating as $item) {
if (is_string($item) && class_exists($item)) {
$chain[] = new $item($tenant); // Classes are instantiated and given $tenant
} elseif ($item instanceof ShouldQueue) {
$chain[] = $item;
}
}
QueuedTenantDatabaseCreator::withChain($chain)->dispatch($manager, $database);
$this->createDatabaseAsynchronously($tenant, $afterCreating);
} else {
$manager->createDatabase($database);
foreach ($afterCreating as $item) {
if (is_object($item) && ! $item instanceof Closure) {
$item->handle($tenant);
} else {
$item($tenant);
}
$this->createDatabaseSynchronously($tenant, $afterCreating);
}
$this->tenancy->event('database.created', $tenant->database()->getName(), $tenant);
}
protected function createDatabaseAsynchronously(Tenant $tenant, array $afterCreating)
{
$chain = [];
foreach ($afterCreating as $item) {
if (is_string($item) && class_exists($item)) {
$chain[] = new $item($tenant); // Classes are instantiated and given $tenant
} elseif ($item instanceof ShouldQueue) {
$chain[] = $item;
}
}
$this->tenancy->event('database.created', $database, $tenant);
QueuedTenantDatabaseCreator::withChain($chain)->dispatch($tenant->database()->manager(), $tenant->database());
}
protected function createDatabaseSynchronously(Tenant $tenant, array $afterCreating)
{
$manager = $tenant->database()->manager();
$manager->createDatabase($tenant->database()->getName());
if ($manager instanceof ManagesDatabaseUsers) {
$manager->createUser($tenant->database());
}
foreach ($afterCreating as $item) {
if (is_object($item) && !$item instanceof Closure) {
$item->handle($tenant);
} else {
$item($tenant);
}
}
}
/**
* Delete a tenant's database.
*
* @param Tenant $tenant
* @return void
*
* @throws DatabaseManagerNotRegisteredException
*/
public function deleteDatabase(Tenant $tenant)
{
$database = $tenant->getDatabaseName();
$manager = $this->getTenantDatabaseManager($tenant);
$database = $tenant->database()->getName();
$manager = $tenant->database()->manager();
$this->tenancy->event('database.deleting', $database, $tenant);
@ -221,50 +184,11 @@ class DatabaseManager
QueuedTenantDatabaseDeleter::dispatch($manager, $database);
} else {
$manager->deleteDatabase($database);
if ($manager instanceof ManagesDatabaseUsers) {
$manager->deleteUser($tenant->database());
}
}
$this->tenancy->event('database.deleted', $database, $tenant);
}
/**
* Get the TenantDatabaseManager for a tenant's database connection.
*
* @param Tenant $tenant
* @return TenantDatabaseManager
* @throws DatabaseManagerNotRegisteredException
*/
public function getTenantDatabaseManager(Tenant $tenant): TenantDatabaseManager
{
$driver = $this->getDriver($this->getBaseConnection($connectionName = $tenant->getConnectionName()));
$databaseManagers = $this->app['config']['tenancy.database_managers'];
if (! array_key_exists($driver, $databaseManagers)) {
throw new DatabaseManagerNotRegisteredException($driver);
}
$databaseManager = $this->app[$databaseManagers[$driver]];
if ($connectionName !== 'tenant' && $databaseManager instanceof CanSetConnection) {
$databaseManager->setConnection($connectionName);
}
return $databaseManager;
}
/**
* What key on the connection config should be used to separate tenants.
*
* @param string $connectionName
* @return string
*/
public function separateBy(string $connectionName): string
{
if ($this->getDriver($this->getBaseConnection($connectionName)) === 'pgsql'
&& $this->app['config']['tenancy.database.separate_by'] === 'schema') {
return 'schema';
}
return 'database';
}
}

View file

@ -9,7 +9,9 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class QueuedTenantDatabaseCreator implements ShouldQueue
{
@ -18,20 +20,13 @@ class QueuedTenantDatabaseCreator implements ShouldQueue
/** @var TenantDatabaseManager */
protected $databaseManager;
/** @var string */
protected $databaseName;
/** @var Tenant */
protected $tenant;
/**
* Create a new job instance.
*
* @param TenantDatabaseManager $databaseManager
* @param string $databaseName
* @return void
*/
public function __construct(TenantDatabaseManager $databaseManager, string $databaseName)
public function __construct(TenantDatabaseManager $databaseManager, Tenant $tenant)
{
$this->databaseManager = $databaseManager;
$this->databaseName = $databaseName;
$this->tenant = $tenant;
}
/**
@ -42,5 +37,9 @@ class QueuedTenantDatabaseCreator implements ShouldQueue
public function handle()
{
$this->databaseManager->createDatabase($this->databaseName);
if ($this->databaseManager instanceof ManagesDatabaseUsers) {
$this->databaseManager->createUser($this->tenant->database());
}
}
}

View file

@ -9,7 +9,9 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
use Stancl\Tenancy\Tenant;
class QueuedTenantDatabaseDeleter implements ShouldQueue
{
@ -18,20 +20,13 @@ class QueuedTenantDatabaseDeleter implements ShouldQueue
/** @var TenantDatabaseManager */
protected $databaseManager;
/** @var string */
protected $databaseName;
/** @var Tenant */
protected $tenant;
/**
* Create a new job instance.
*
* @param TenantDatabaseManager $databaseManager
* @param string $databaseName
* @return void
*/
public function __construct(TenantDatabaseManager $databaseManager, string $databaseName)
public function __construct(TenantDatabaseManager $databaseManager, Tenant $tenant)
{
$this->databaseManager = $databaseManager;
$this->databaseName = $databaseName;
$this->tenant = $tenant;
}
/**
@ -41,6 +36,9 @@ class QueuedTenantDatabaseDeleter implements ShouldQueue
*/
public function handle()
{
$this->databaseManager->deleteDatabase($this->databaseName);
$this->databaseManager->deleteDatabase($this->tenant->database()->getName());
if ($this->databaseManager instanceof ManagesDatabaseUsers) {
$this->databaseManager->deleteUser($this->tenant->database());
}
}
}

View file

@ -21,8 +21,8 @@ class DatabaseTenancyBootstrapper implements TenancyBootstrapper
public function start(Tenant $tenant)
{
$database = $tenant->getDatabaseName();
if (! $this->database->getTenantDatabaseManager($tenant)->databaseExists($database)) {
$database = $tenant->database()->getName();
if (! $tenant->database()->manager()->databaseExists($database)) {
throw new TenantDatabaseDoesNotExistException($database);
}

View file

@ -19,6 +19,7 @@ use Stancl\Tenancy\Exceptions\TenantStorageException;
/**
* @internal Class is subject to breaking changes in minor and patch versions.
*/
// todo make this class serializable
class Tenant implements ArrayAccess
{
use Traits\HasArrayAccess,
@ -91,8 +92,7 @@ class Tenant implements ArrayAccess
}
/**
* DO NOT CALL THIS METHOD FROM USERLAND. Used by storage
* drivers to create persisted instances of Tenant.
* Used by storage drivers to create persisted instances of Tenant.
*
* @param array $data
* @return self
@ -272,23 +272,13 @@ class Tenant implements ArrayAccess
}
/**
* Get the tenant's database's name.
* Get database config.
*
* @return string
* @return DatabaseConfig
*/
public function getDatabaseName(): string
public function database(): DatabaseConfig
{
return $this->data['_tenancy_db_name'] ?? ($this->config->get('tenancy.database.prefix') . $this->id . $this->config->get('tenancy.database.suffix'));
}
/**
* Get the tenant's database connection's name.
*
* @return string
*/
public function getConnectionName(): string
{
return $this->data['_tenancy_db_connection'] ?? 'tenant';
return new DatabaseConfig($this);
}
/**

View file

@ -8,9 +8,10 @@ use Illuminate\Contracts\Config\Repository;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\DB;
use Stancl\Tenancy\Contracts\Future\CanSetConnection;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\Contracts\TenantDatabaseManager;
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection, ManagesDatabaseUsers
{
/** @var string */
protected $connection;
@ -20,6 +21,11 @@ class MySQLDatabaseManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $config->get('tenancy.database_manager_connections.mysql');
}
public function getSeparator(): string
{
return 'database';
}
protected function database(): Connection
{
return DB::connection($this->connection);

View file

@ -0,0 +1,47 @@
<?php
namespace Stancl\Tenancy\TenantDatabaseManagers;
use Stancl\Tenancy\Contracts\ManagesDatabaseUsers;
use Stancl\Tenancy\DatabaseConfig;
class PermissionControlledMySQLDatabaseManager extends MySQLDatabaseManager implements ManagesDatabaseUsers
{
public static $grants = [
'ALTER', 'ALTER ROUTINE', 'CREATE', 'CREATE ROUTINE', 'CREATE TEMPORARY TABLES', 'CREATE VIEW',
'DELETE', 'DROP', 'EVENT', 'EXECUTE', 'INDEX', 'INSERT', 'LOCK TABLES', 'REFERENCES', 'SELECT',
'SHOW VIEW', 'TRIGGER', 'UPDATE',
];
public function createUser(DatabaseConfig $databaseConfig): void
{
$database = $databaseConfig->getName();
$username = $databaseConfig->getUsername();
$hostname = $databaseConfig->connection()['host'];
$password = $databaseConfig->getPassword();
$this->database()->statement("CREATE USER `{$username}`@`{$hostname}` IDENTIFIED BY `{$password}`");
$grants = implode(', ', static::$grants);
if ($this->isVersion8()) { // MySQL 8+
$grantQuery = "GRANT $grants ON `$database`.* TO `$username`@`$hostname`";
} else { // MySQL 5.7
$grantQuery = "GRANT $grants ON $database.* TO `$username`@`$hostname` IDENTIFIED BY '$password'";
}
$this->database()->statement($grantQuery);
}
protected function isVersion8(): bool
{
$version = $this->database()->select($this->db->raw('select version()'))[0]->{'version()'};
return version_compare($version, '8.0.0') >= 0;
}
public function deleteUser(DatabaseConfig $databaseConfig): void
{
$this->database()->statement("DROP USER IF EXISTS '{$databaseConfig->username}'");
}
}

View file

@ -20,6 +20,11 @@ class PostgreSQLDatabaseManager implements TenantDatabaseManager, CanSetConnecti
$this->connection = $config->get('tenancy.database_manager_connections.pgsql');
}
public function getSeparator(): string
{
return 'database';
}
protected function database(): Connection
{
return DB::connection($this->connection);

View file

@ -20,6 +20,11 @@ class PostgreSQLSchemaManager implements TenantDatabaseManager, CanSetConnection
$this->connection = $config->get('tenancy.database_manager_connections.pgsql');
}
public function getSeparator(): string
{
return 'schema';
}
protected function database(): Connection
{
return DB::connection($this->connection);

View file

@ -8,6 +8,11 @@ use Stancl\Tenancy\Contracts\TenantDatabaseManager;
class SQLiteDatabaseManager implements TenantDatabaseManager
{
public function getSeparator(): string
{
return 'database';
}
public function createDatabase(string $name): bool
{
try {

View file

@ -32,12 +32,12 @@ class DatabaseManagerTest extends TestCase
}
/** @test */
public function the_default_db_is_used_when_based_on_is_null()
public function the_default_db_is_used_when_template_connection_is_null()
{
$this->assertSame('sqlite', config('database.default'));
config([
'database.connections.sqlite.foo' => 'bar',
'tenancy.database.based_on' => null,
'tenancy.database.template_connection' => null,
]);
tenancy()->init('test.localhost');

View file

@ -19,9 +19,8 @@ class DatabaseSchemaManagerTest extends TestCase
'database.default' => 'pgsql',
'database.connections.pgsql.database' => 'main',
'database.connections.pgsql.schema' => 'public',
'tenancy.database.based_on' => null,
'tenancy.database.template_connection' => null,
'tenancy.database.suffix' => '',
'tenancy.database.separate_by' => 'schema',
'tenancy.database_managers.pgsql' => \Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class,
]);
}
@ -41,14 +40,14 @@ class DatabaseSchemaManagerTest extends TestCase
}
/** @test */
public function the_default_db_is_used_when_based_on_is_null()
public function the_default_db_is_used_when_template_connection_is_null()
{
config(['database.default' => 'pgsql']);
$this->assertSame('pgsql', config('database.default'));
config([
'database.connections.pgsql.foo' => 'bar',
'tenancy.database.based_on' => null,
'tenancy.database.template_connection' => null,
]);
tenancy()->init('test.localhost');
@ -63,7 +62,7 @@ class DatabaseSchemaManagerTest extends TestCase
$tenant = tenancy()->create(['schema.localhost']);
tenancy()->init('schema.localhost');
$this->assertSame($tenant->getDatabaseName(), config('database.connections.' . config('database.default') . '.schema'));
$this->assertSame($tenant->database()->getName(), config('database.connections.' . config('database.default') . '.schema'));
}
/** @test */

View file

@ -0,0 +1,24 @@
<?php
namespace Stancl\Tenancy\Tests;
class DatabaseUsersTest extends TestCase
{
/** @test */
public function users_are_created_when_permission_controlled_mysql_manager_is_used()
{
}
/** @test */
public function correct_grants_are_given_to_the_users()
{
}
/** @test */
public function having_existing_databases_without_users_and_switching_to_permission_controlled_mysql_manager_doesnt_break_existing_dbs()
{
}
}

View file

@ -180,7 +180,7 @@ class TenantManagerTest extends TestCase
'_tenancy_db_name' => $database,
]);
$this->assertSame($database, $tenant->getDatabaseName());
$this->assertSame($database, $tenant->database()->getName());
}
/** @test */

View file

@ -82,7 +82,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
'database' => database_path('central.sqlite'),
],
'tenancy.database' => [
'based_on' => 'sqlite',
'template_connection' => 'sqlite',
'prefix' => 'tenant',
'suffix' => '.sqlite',
],