1

Folks,

I've been trying to overcome this issue for more than 10 hours now and I've decided to look for help before going insane.

What I'm trying to achieve is some sort multi-tenancy where I have a dedicated database for each customer and depending on the issuer of the request the concrete database should be loaded. E.g. if customer A sends a request, load database A, if B sends the request, load database B. We have thousands of customers, so having all configs predefined in the databse.php file is not an option.

In a few special cases, the same database should be loaded irrespective of the issuer. E.g. if the request is X, always load database C.

I have the following architecture: BaseModel which Extends Eloquent Model. Application models which extend BaseModel. A few libraries which talk to the Application models.

Since all my database requests go through BaseModel, the best solution that I thought would work was to insert the code which switches to the correct database in the BaseModel's constructor. E.g.:

class BaseModel extends \Eloquent { 


public function __construct(array $attributes = array()) {     

    $currentConfigs = \Config::get('database.connections.customers');    

    $currentConfigs['database'] = 'db_A'; //this varies depending on the request
    $currentConfigs['prefix'] = 'custName'; //this varies depending on the request                               

    \Config::set('database.connections.customers', $currentConfigs); 

    parent::__construct($attributes);        
} 

The problem that I am facing is that I seem to be able to set the configs only once. In other words, if I only need database A or B throughout the lifecycle of the request, all works fine. Though, if I need to switch to database C, this doesn't work. BaseModel continues to look for the required tables in A or B, even though I tried to explicitly load C. It looks as if the db settings are immutable or something like that.

That said, I have a few questions:

  1. What is the best way to dynamically load different databases run-time, having in mind that one request may need to query multiple databases and not just one.

  2. How can I unload or reset the database connection, so that I set a new one?

  3. Which method in Laravel can give me information about the current db connection? I need ways to debug this, but I'm totally blind to the possible ways to go about that.

Any help/tips would be appreciated.

Mita Ka
  • 317
  • 5
  • 8
  • Have a look at this [Connect multiple databases dynamically in laravel](https://stackoverflow.com/questions/51074804/connect-multiple-databases-dynamically-in-laravel/52731820#52731820) – Fred Lai Oct 10 '18 at 03:16

1 Answers1

6

The problem is, that Laravel establishes a connection using the config and then keeps it in memory. If you want to do it by setting new connection variables, you have to reconnect after making changes. (also note that you can set the config directly with the awesome dot notation)

\Config::set('database.connections.customers.database', 'db_A');
DB::reconnect('customers');

However the better way, in my opinion, is to just use SQL to switch the database

DB::unprepared('USE db_A');

To get the current database name you can use SQL as well

DB::select('SELECT DATABASE()')[0]->{"DATABASE()"} // DATABASE() might be something else if you're not using MySQL

Update - Table prefix

An easy way to make the table prefix dynamic too, would be to add this in your BaseModel constructor

$this->setTable('custName'.$this->getTable());
lukasgeiter
  • 147,337
  • 26
  • 332
  • 270
  • Awesome, I think this was 95% of the missing piece. I really want to accept your answer, but there seems to be one more issue :( After DB::reconnect, the table prefix seems to be ignored. Any ideas why is that? I didn't mention that before, as I thought it was irrelevant, but some of my tables have prefix and others don't and I need the flexibility to be able to set these runtime... – Mita Ka Nov 15 '14 at 21:54
  • Hmmm.. maybe try `DB::setDefaultConnection('customers');` after reconnect. (No idea why but I found that in an online forum) And what if you use the `USE db_A` method? – lukasgeiter Nov 15 '14 at 22:00
  • setDefaultConnection didn't do the trick :( I will try the USE db_A method, but I'm wondering if it will keep the prefixes... Will try and will update the topic in a while. – Mita Ka Nov 15 '14 at 22:48
  • It's the same, the prefix gets ignored... For the moment the only solution would be to introduce the prefix before we enter the BaseModel constructor. It's not as clean as I wish, but unless there are any other suggestions, this is what I will most likely end up doing... – Mita Ka Nov 15 '14 at 23:07
  • Weird.. I just tried it and it all worked fine. You do have defined your prefixes in the database config, don't you? – lukasgeiter Nov 15 '14 at 23:15
  • Not really. Since we have a prefix for each customer, it would be ugly if we put everything in the database config. The prefix is empty with the database config, but I set it with the code above (I didn't put that for simplicity, but will update the question). To summarize: My prefix configuration is empty, but I set it via the code in BaseModel (and it doesn't work)... – Mita Ka Nov 15 '14 at 23:26
  • I see... well I have no idea why it is ignored when changing databases, but I updated my answer with an explanation how I would work around that issue. – lukasgeiter Nov 15 '14 at 23:35
  • I don't have enough words to thank you :) setTable saved the day! – Mita Ka Nov 15 '14 at 23:44
  • To anyone searching for the same problem I've solved the prefix problem by making DB::purge(DB::getDefaultConnection()); DB::Reconnect($new_connection); – RuntimeError Apr 16 '15 at 15:13