getTenantKey() . config('tenancy.database.suffix'); }; } } public function __construct(Model&Tenant $tenant, array $databaseConfig) { static::__constructStatic(); $this->tenant = $tenant; $this->tenantConfig = $databaseConfig; } public static function generateDatabaseNamesUsing(Closure $databaseNameGenerator): void { static::$databaseNameGenerator = $databaseNameGenerator; } public static function generateUsernamesUsing(Closure $usernameGenerator): void { static::$usernameGenerator = $usernameGenerator; } public static function generatePasswordsUsing(Closure $passwordGenerator): void { static::$passwordGenerator = $passwordGenerator; } public function getName(): string { return $this->tenant->getInternal('db_name') ?? (static::$databaseNameGenerator)($this->tenant); } public function getUsername(): ?string { return $this->tenant->getInternal('db_username') ?? null; } public function getPassword(): ?string { return $this->tenant->getInternal('db_password') ?? null; } /** * Generate DB name, username & password and write them to the tenant model. */ public function makeCredentials(): void { $this->tenant->setInternal('db_name', $this->getName()); if ($this->connectionDriverManager($this->getTemplateConnectionDriver()) instanceof Contracts\ManagesDatabaseUsers) { $this->tenant->setInternal('db_username', $this->getUsername() ?? (static::$usernameGenerator)($this->tenant)); $this->tenant->setInternal('db_password', $this->getPassword() ?? (static::$passwordGenerator)($this->tenant)); } if ($this->tenant->exists) { $this->tenant->save(); } } public function getTemplateConnectionDriver(): string { return $this->getTemplateConnection()['driver']; } public function getTemplateConnection(): array { if ($template = $this->tenant->getInternal('db_connection')) { return config("database.connections.{$template}"); } if ($template = config('tenancy.database.template_tenant_connection')) { return is_array($template) ? array_merge($this->getCentralConnection(), $template) : config("database.connections.{$template}"); } return $this->getCentralConnection(); } protected function getCentralConnection(): array { $centralConnectionName = config('tenancy.database.central_connection'); return config("database.connections.{$centralConnectionName}"); } public function getTenantHostConnectionName(): string { return config('tenancy.database.tenant_host_connection_name', 'tenant_host_connection'); } /** * Tenant's own database connection config. */ public function connection(): array { $templateConnection = $this->getTemplateConnection(); return $this->manager()->makeConnectionConfig( array_merge($templateConnection, $this->tenantConfig), $this->getName() ); } /** * Tenant's host database connection config. */ public function hostConnection(): array { $config = $this->tenantConfig; $templateConnection = $this->getTemplateConnection(); if ($this->connectionDriverManager($this->getTemplateConnectionDriver()) instanceof Contracts\ManagesDatabaseUsers) { // We're removing the username and password because user with these credentials is not created yet // If you need to provide username and password when using PermissionControlledMySQLDatabaseManager, // consider creating a new connection and use it as `tenancy_db_connection` tenant config key unset($config['username'], $config['password']); } if (! $config) { return $templateConnection; } return array_replace($templateConnection, $config); } /** * Purge the previous tenant connection before opening it for another tenant. */ public function purgeHostConnection(): void { DB::purge($this->getTenantHostConnectionName()); } /** Get the TenantDatabaseManager for this tenant's connection. * * @throws NoConnectionSetException|DatabaseManagerNotRegisteredException */ public function manager(): Contracts\TenantDatabaseManager { // Laravel caches the previous PDO connection, so we purge it to be able to change the connection details $this->purgeHostConnection(); // Create the tenant host connection config $tenantHostConnectionName = $this->getTenantHostConnectionName(); config(["database.connections.{$tenantHostConnectionName}" => $this->hostConnection()]); $manager = $this->connectionDriverManager(config("database.connections.{$tenantHostConnectionName}.driver")); if ($manager instanceof Contracts\StatefulTenantDatabaseManager) { $manager->setConnection($tenantHostConnectionName); } return $manager; } /** * todo@name come up with a better name * Get database manager class from the given connection config's driver. * * @throws DatabaseManagerNotRegisteredException */ protected function connectionDriverManager(string $driver): Contracts\TenantDatabaseManager { $databaseManagers = config('tenancy.database.managers'); if (! array_key_exists($driver, $databaseManagers)) { throw new DatabaseManagerNotRegisteredException($driver); } return app($databaseManagers[$driver]); } }