Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

uxweb's avatar
Level 20

Change database name of connection on the fly

Hi friends!.

I am working on a new application that must connect to several databases. The number of databases are huge and in some point the models of my app should point to some of this databases.

My first approach will be to define n connections per database in my database.php configuration file, but i think this is not really good cause there are already like 150 databases and often a new one is created, having me to edit the file to add a new connection.

Fortunately there is a database which keeps a list of all the databases, think i can create a model that points to that table and one connection for that.

That will let me present to the user a list of all the databases that exists, and then let the user choose one database that will be the context under all the models will work, i can create another configuration connection for this purpose and change only the database name on the fly according to the database the user has selected.

I think this is pretty much like the tenant app, but is not equal, think this should be more simple.

I think about the on method provided by Eloquent Model, but cause i am not defining every single connection for every single database (which will be a mess) i can´t use it.

Currently i can store the database name in session, what i cant figure out how to do is to tell eloquent models to operate on that database.

Any help will be appreciated.

Thank you!!

0 likes
29 replies
xingfucoder's avatar

Hi @uxweb,

I don't know if this answer give you the solution but I would make these steps:

  1. Create a DatabaseServerNames API for query your Server databases you need with i.e. a following result:
{
  "dbconnection1": [
        "dbhost": "111.111.111.111",
        "dbname": "db1",
        "role_needed": "admin"
        //You probably could get more data
       ],
  "dbconnection2": [
        "dbhost": "111.111.111.111",
        "dbname": "db2",
        "role_needed": "manager"
        //More data
       ],
}
  1. Load this API with a Package within your project.

  2. Query this API for get all the databases using an Application or Domain Services (it will depends on your needs, if the needs of get the connection is related with your Business Rules, put within the Domain).

  3. Optionally give access through some token or rule within your connection API response to the users (as the role_needed). If the users is allowed to connect to some database it will be in an available connection lists.

PD. Using the Illuminate/Database/DatabaseManager.php or the DB facade you would have access to the connections.

Maybe it doesn't the expected answer but you could take some tips.

Hope it helps.

1 like
uxweb's avatar
Level 20

@codeatbusiness Wow thank you, i was thinking to override some methods of DatabaseManager in a new class like MyDatabaseManager, but after doing that, where i can tell Laravel to use this implementation instead the default?

I see that DatabaseManager implements ConnectionResolverInterface, but that does not tell me anything :s.

Do you know how to do this?

Thank you again!

uxweb's avatar
Level 20

@codeatbusiness Thank you, i have read it right now, but it looks obscure for me.

Trying to make my mind on this:

There is a DatabaseManager that can receive new "driver" implementation?

Then, i can see that DatabaseManager uses a ConnectionResolver and think this is the "driver" i must implement and pass to DatabaseManager::extend().

What i don´t get is that ConnectionResolver interface only has this methods:

connection($name=null)

getDefaultConnection($name)

setDefaultConnection($name)

Using this methods i can´t access the database name "key" in a connection configuration, it only gets and sets the connection name, let´s put this:

'compac' => [
            'driver'   => 'sqlsrv',
            'host'     => env('DB_HOST_COMPAC'),
            'database' => env('DB_DATABASE_COMPAC'),
            'username' => env('DB_USERNAME_COMPAC'),
            'password' => env('DB_PASSWORD_COMPAC'),
            'prefix'   => '',
        ],

Before the database manager creates a new connection with the data in the configuration, i would like to acces the 'database' key and set it for the one i want, that will be on the fly and after that let the DatabaseManager does what it does best :).

Now, i can see that DatabaseManager has a "getConfig()" method, which is where it reads the database configuration from the configuration file, that seems to me the best place to put this logic, but don´t know how to do it.

Hope this helps you inderstand what i want to achieve.

Thank you!

xingfucoder's avatar

Hi, @uxweb,

Alternatively to the extending process, you could use a template Database config connection that connects to your Databases API not using the env() method but using the Config facade.

uxweb's avatar
Level 20

@codeatbusiness ohh, i think i don't get that last one about template database config. Do you refer to the database.php config file?

Can i use a facade that let me get the database name of the connection and set it like this?

'compac' => [
            'driver'   => 'sqlsrv',
            'host'     => env('DB_HOST_COMPAC'),
            'database' => ContextFacade::getDatabaseName(),
            'username' => env('DB_USERNAME_COMPAC'),
            'password' => env('DB_PASSWORD_COMPAC'),
            'prefix'   => '',
        ],

Is this what you mean?

Thank you!

xingfucoder's avatar

Yes, sorry that file.

Using that facade you could have a custom method to get a list of connection bases in some criteria (role, location, host,...)

uxweb's avatar
Level 20

@codeatbusiness Ohh i see, i am trying to do it but it gives me this error:

Fatal error: Call to a member function getDatabaseName() on null in /home/vagrant/Code/hermes/clasificacion-contable/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php on line 207

Think that it can't create an instance of the facade or something like this, this is weird

kodeine's avatar

I have did this before with slim framework. I achieved this having a database service. Each user had his own database. I had a User_Storage table which stored db connection hosts, db names etc. Models which need to use that Database could use UserStorage Service. That way each user can use his own db storage.

1 like
uxweb's avatar
Level 20

@codeatbusiness I have created my Facade like this

<?php namespace Ghi\Domain\Core\Facades;

use Illuminate\Support\Facades\Facade;

class Context extends Facade {

    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'ghi.context'; }
}

And i already have created the ContextServiceProvider and added it to the providers section at app.php con file.

uxweb's avatar
Level 20

@kodeine I think that for a most complex app that should work, in this case, every single user has access to all the databases, there are no roles or profiles and there is no mapping between db's and users.

Can you show the components you wrote to achieve this in your slim framework app?

I appreciate your help! :)

uxweb's avatar
Level 20

@codeatbusiness I want to thank you for taking me to the idea to set on the fly the database name of the dynamic connection.

What i did worked well, but still don't know if there is a better approach, here it is:

  • Have a ContextService to keep in session which database the model's database connection should be in context of.
<?php namespace Ghi\Providers;

use Illuminate\Support\ServiceProvider;

class ContextServiceProvider extends ServiceProvider {

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'Ghi\Domain\Core\Services\Context',
            'Ghi\Domain\Core\Services\ContextSession'
        );

        $this->app->bindShared('ghi.context', function()
        {
            return $this->app->make('Ghi\Domain\Core\Services\ContextSession');
        });

    }
}
  • Have a connection used only by models that must be set in a dynamic database connection context.
'compac' => [
        'driver'   => 'sqlsrv',
        'host'     => env('DB_HOST_COMPAC'),
        'database' => env('DB_DATABASE_COMPAC'),
        'username' => env('DB_USERNAME_COMPAC'),
        'password' => env('DB_PASSWORD_COMPAC'),
        'prefix'   => '',
],

Even if by default i'm setting the database name via a environment variable, with the BaseRepository i can read this and set it to other database name on the fly.

  • Have a BaseRepository which will set the database name of the model before it establishes a connection.
<?php  namespace Ghi\Domain\Core; 

use Ghi\Domain\Core\Services\Context;
use Illuminate\Config\Repository;

abstract class BaseRepository {

    /**
     * @var Context
     */
    protected $context;

    /**
     * @var Repository
     */
    private $config;

    /**
     * @param Context $context
     * @param Repository $config
     */
    function __construct(Context $context, Repository $config)
    {
        $this->context = $context;
        $this->config = $config;
        $this->config->set('database.connections.compac.database', $this->context->getDatabaseName());
    }
}

Here is where i set the database name on the fly using the database configuration.

  • Then a Repository that extends BaseRepository
<?php  namespace Ghi\Infraestructure\Polizas; 

use Ghi\Domain\Core\BaseRepository;
use Ghi\Domain\Polizas\MovimientoPoliza;
use Ghi\Domain\Polizas\Poliza;
use Ghi\Domain\Polizas\PolizaRepository;

class EloquentPolizaRepository extends BaseRepository implements PolizaRepository {

    /**
     * Obtiene todas las polizas
     *
     * @return mixed
     */
    public function getAll()
    {
        return Poliza::all();
    }

    /**
     * Obtiene todas las polizas paginadas
     *
     * @param int $howMany
     * @return mixed
     */
    public function getAllPaginated($howMany = 50)
    {
        return Poliza::orderByRaw('Fecha DESC, Folio ASC' )
            ->paginate($howMany);
    }

    /**
     * Obtiene los movimientos de una poliza por id
     *
     * @param $polizaId
     * @return mixed
     */
    public function getMovimientos($polizaId)
    {
        return MovimientoPoliza::where('IdPoliza', $polizaId)->get();
}

    /**
     * Obtiene una poliza por su id
     *
     * @param $id
     * @return mixed
     */
    public function getById($id)
    {
        return Poliza::find($id);
    }

    /**
     * @param $folio
     * @return mixed
     */
    public function getByFolio($folio)
    {
        return Poliza::where('Folio', $folio)->firstOrFail();
    }}
  • Finally the Eloquent model used in the repository
<?php namespace Ghi\Domain\Polizas;

use Carbon\Carbon;
use Ghi\Domain\Core\TenantModel;
use Illuminate\Database\Eloquent\Model;

class Poliza extends Model {

    /**
     * @var string
     */
    protected $connection = 'compac';

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'Polizas';

    /**
     * @var string
     */
    protected $primaryKey = 'Id';

    /**
     * @var bool
     */
    public $timestamps = false;

    /**
     * @var array
     */
    protected $dates = ['Fecha'];

Every single model that should work on any database, needs the $connection attribute to be set to 'compac' because that is the connection "template" to be used to change its database name.

This was my solution until now, i think it is simple and clean, i am not a software engineer or software architect and think this can be done better.

What do you think about it?

Thank you!

xingfucoder's avatar

Hi @uxweb, I am not either software engineer :-) I think your way is good for your requirements. I thought you was needing several Connections per user.

3 likes
uxweb's avatar
Level 20

Thank you everybody, i'll wait for someone else to show some more ideas about this and after i'll mark this discussion as answered.

Glad i have your help guys! :D

JarekTkaczyk's avatar

@uxweb Can you explain the use-case for this? I wonder about the reason that you let each user choose the database he will be using and how/for what it will be used.

uxweb's avatar
Level 20

@JarekTkaczyk Yep, the use-case is this:

There is an account system that handles many databases. When a user creates a new company/business, the system creates a new database to store information about it.

The system has Roles and Permissions defined, but that part is not relevant for the Laravel App i am building.

A requirement is that any user that can authenticate in the Laravel App can see every single one of these databases, can choose one and view data of the database (via models).

Then a user can change his/her mind and might need to change to other database to browse some data.

Here i can use the term database and company interchangeably to day "a user can access the data from every company database"

That is why i need to present to the user a list of all the companies/databases and let him choose which one to use to browse data.

All the databases have the same schema, so, there is no problem in switching to other on the fly, models will work good pointing to the same table.

The first problem i found beginning building the app was that there is a high number of databases (somenthing like 150), that is a huge problem for me because i will have to edit the database.php config file each time a new company is created.

And believe me, maintain a huge configuration file with 150+ connections does not look fun :).

Alrigh, i think this is pretty much what it needs to do.

Hope this makes it more clear.

Thank you!

JarekTkaczyk's avatar

@uxweb I believe you have predefined model for mapping company - database? A naming convention of some sort that you can rely on?

uxweb's avatar
Level 20

@JarekTkaczyk Actually i have my models point to a "connection" and a table, this is a real example

<?php namespace Ghi\Domain\Polizas;

use Carbon\Carbon;
use Ghi\Domain\Core\TenantModel;
use Illuminate\Database\Eloquent\Model;

class Poliza extends Model {

    /**
     * @var string
     */
    protected $connection = 'compac';

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'Polizas';

    /**
     * @var string
     */
    protected $primaryKey = 'Id';

    /**
     * @var bool
     */
    public $timestamps = false;

    /**
     * @var array
     */
    protected $dates = ['Fecha'];
}

As all the databases have the same schema, i don't need to create a model for each company.

The model will work on with any of these databases.

JarekTkaczyk's avatar

@uxweb No, I wasn't talking about eloquent model for each company, but a predefined way of naming database for a company, ie.:

company: name XYZ, vat 1234
database: 1234
uxweb's avatar
Level 20

@JarekTkaczyk Yes, they use some kind of naming convention like:

ctOAT910806EC1_011

ctOAT910806EC1_012

ctOAT910806EC1_013

ctCPM070314K95_000

I think they are defined by the company name, creation date and something else i don't rally know.

eskiesirius's avatar

May I know what is inside of your 'Ghi\Domain\Core\Services\Context' and 'Ghi\Domain\Core\Services\ContextSession'?

jobzella's avatar

use config:set() to set your database connection

if you use sqllite change your database like this

    Config::set("database.connections.sqlite.database", $sqlPath );
tsipis's avatar

@uxweb I know it has been a while but I am dealing with the exact same situation as you have here. I am having a bit of a hard time to follow what you did to resolve it though. Would you be able to provide some more help on this? Thank you in advance!

varuninorbit's avatar

I am new to Laravel. I also faced same problem. In my application a user need to access many databases.

User chooses a choice, a cookie is placed and user is redirect to the home page.

A function uses this cookie to change the database in config before loading all configurations. I know it is not a good idea but it's working till I get a better solution.

xsmalbil@icloud.com's avatar

In the next year I will be moving towards an multidatabase cluster, where every account will have it's own database+migrations and we'll be having one main database which holds reference to all accounts.

Does anybody have any tips on managing multiple databases, using cross-database foreign key relationships?

1 like
mittalvishesh's avatar

Has anyone tried the following? This should be more efficient as it won't have to re-connect to the database.

DB::table("products")->get(); //tenant A
DB::statement("use tenant_b");
DB::table("products")->get(); //tenant B
Tray2's avatar

@mittalvishesh Not sure why you wrote this in a six year old thread but the most efficient way is to use only one database and a scope for which tenant you should filter on.

Please or to participate in this conversation.