#Better way to handle multiple tenant databases?

5 messages · Page 1 of 1 (latest)

slim basin
#

We currently have a structure where each of our tenants has their own database, we also have one system database where we store the tenant organizations, almost everything in the application is based on this Tenant model which lives in the system database, and it has a bunch of relations connected to it on the tenant connection.

The way this tenant connection is described is as follows:

        'tenant' => [
            'driver' => 'mysql',
            'url' => env('DB_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_TENANT', 'tenant_001'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'prefix_indexes' => true,
            'strict' => true,
            'engine' => null,
            'options' => extension_loaded('pdo_mysql') ? array_filter([
                PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
            ]) : [],
        ],

We then have some code on the Tenant model to set up the tenant connection

#
    public function establishTenantConnection()
    {
        $defaultUsername = config('database.connections.tenant.username');
        $defaultPassword = config('database.connections.tenant.password');

        $oldTenantDatabase = config('database.connections.tenant.database');

        $dbName = $this->database->name;
        $username = env('DB_TENANT_USERNAME_'. $dbName, $defaultUsername);
        $password = env('DB_TENANT_PASSWORD_'. $dbName, $defaultPassword);

        config(['database.connections.tenant.database' => $dbName]);
        config(['database.connections.tenant.username' => $username]);
        config(['database.connections.tenant.password' => $password]);

        // Purge the current tenant connection and reconnect tenant if database is changed
        if ($oldTenantDatabase != $dbName) {
            DB::purge('tenant');
            DB::reconnect('tenant');
        } else {
            DB::connection('tenant');
        }
    }
#

This sets the username and password for the connection based on another relation (database) that's connected to the tenant

#

Now for every relation on the Tenant model, this function is called to establish the tenant connection

    public function systemSettings(): HasOne
    {
        $this->establishTenantConnection();
        return $this->hasOne(TenantSystemSettings::class);
    }
#

Somehow, this is giving me issues with eager loading relations. The current tenant mostly comes from the route parameter {tenant} and is resolved by Laravel to the correct model. In the resolveRouteBinding function on the Tenant model, I cannot eager load any relations that are stored in the other database.