1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-02-04 21:24:04 +00:00
tenancy/src/Database/DatabaseManager.php
Samuel Štancl 48b916e182
Supported named in-memory SQLite databases (#69)
This PR adds support for named in-memory SQLite databases, making it feasible to use in-memory SQLite for tenant databases in tests.

The usage is simply creating a tenant with 'tenancy_db_name' => ':memory:' and the bootstrapper will automatically update the tenant with a database name derived from its tenant key.

There are static property hooks for keeping these DBs alive (at least one connection needs to be open, they don't have process lifetime and are essentially "refcounted") and closing them when the database is deleted. This gives the user control over the lifetimes of these databases.
2024-11-25 04:48:52 +01:00

89 lines
2.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Database;
use Illuminate\Config\Repository;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
use Stancl\Tenancy\Contracts\TenantCannotBeCreatedException;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\TenantDatabaseManagers\SQLiteDatabaseManager;
/**
* @internal Class is subject to breaking changes in minor and patch versions.
*/
class DatabaseManager
{
public function __construct(
protected Application $app,
protected BaseDatabaseManager $database,
protected Repository $config,
) {}
/** Connect to a tenant's database. */
public function connectToTenant(TenantWithDatabase $tenant): void
{
$this->purgeTenantConnection();
$this->createTenantConnection($tenant);
$this->setDefaultConnection('tenant');
}
/** Reconnect to the default non-tenant connection. */
public function reconnectToCentral(): void
{
$this->purgeTenantConnection();
$this->setDefaultConnection($this->config->get('tenancy.database.central_connection'));
}
/** Change the default database connection config. */
public function setDefaultConnection(string $connection): void
{
$this->config['database.default'] = $connection;
$this->database->setDefaultConnection($connection);
}
/** Create the tenant database connection. */
public function createTenantConnection(TenantWithDatabase $tenant): void
{
$this->config['database.connections.tenant'] = $tenant->database()->connection();
}
/** Purge the tenant database connection. */
public function purgeTenantConnection(): void
{
if (array_key_exists('tenant', $this->database->getConnections())) {
$this->database->purge('tenant');
}
unset($this->config['database.connections.tenant']);
}
/**
* Check if a tenant can be created.
*
* @throws TenantCannotBeCreatedException
* @throws Exceptions\DatabaseManagerNotRegisteredException
* @throws Exceptions\TenantDatabaseAlreadyExistsException
*/
public function ensureTenantCanBeCreated(TenantWithDatabase $tenant): void
{
$manager = $tenant->database()->manager();
if ($manager->databaseExists($database = $tenant->database()->getName())) {
if (! $manager instanceof SQLiteDatabaseManager || ! SQLiteDatabaseManager::isInMemory($database)) {
throw new Exceptions\TenantDatabaseAlreadyExistsException($database);
}
}
if ($manager instanceof Contracts\ManagesDatabaseUsers) {
/** @var string $username */
$username = $tenant->database()->getUsername();
if ($manager->userExists($username)) {
throw new Exceptions\TenantDatabaseUserAlreadyExistsException($username);
}
}
}
}