如何覆盖Laravel的数据库连接
如何覆盖Laravel的数据库连接
大家好,
我已经花了超过10个小时试图解决这个问题,现在我决定在发疯之前寻求帮助。
我想要实现的是一种多租户的方式,即为每个客户分配一个专用数据库,并根据请求的发起者加载相应的数据库。例如,如果客户A发送请求,加载数据库A;如果客户B发送请求,加载数据库B。由于我们有成千上万个客户,所以在database.php文件中预定义所有配置不可行。
在一些特殊情况下,无论请求的发起者如何,都应该加载相同的数据库。例如,如果请求是X,总是加载数据库C。
我有以下架构:
BaseModel继承自Eloquent Model。
应用模型继承自BaseModel。
一些与应用模型交互的库。
由于我所有的数据库请求都经过BaseModel,我认为可能会起作用的最好解决方案是在BaseModel的构造函数中插入切换到正确数据库的代码。例如:
class BaseModel extends \Eloquent { public function __construct(array $attributes = array()) { $currentConfigs = \Config::get('database.connections.customers'); $currentConfigs['database'] = 'db_A'; //根据请求的不同而变化 $currentConfigs['prefix'] = 'custName'; //根据请求的不同而变化 \Config::set('database.connections.customers', $currentConfigs); parent::__construct($attributes); }
我面临的问题是我似乎只能设置配置一次。换句话说,如果在请求的生命周期内只需要数据库A或B,一切都正常。但是,如果我需要切换到数据库C,这就不起作用了。BaseModel在A或B中继续寻找所需的表,即使我试图显式加载C。看起来数据库设置是不可变的或类似于这样的东西。
也就是说,我有几个问题:
1. 在运行时动态加载不同的数据库的最佳方法是什么,要考虑到一个请求可能需要查询多个数据库而不仅仅是一个?
2. 如何卸载或重置数据库连接,以便设置一个新的连接?
3. Laravel中的哪个方法可以给我关于当前数据库连接的信息?我需要调试的方法,但我对可能的方法一无所知。
任何帮助/提示将不胜感激。
如何覆盖Laravel的数据库连接
问题的原因是,Laravel在配置文件中建立了一个连接,并将其保存在内存中。如果要通过设置新的连接变量来实现,需要在进行更改后重新连接。(还要注意,您也可以使用强大的点表示法直接设置配置)
\Config::set('database.connections.customers.database', 'db_A'); DB::reconnect('customers');
然而,我认为更好的方法是使用SQL来切换数据库
DB::unprepared('USE db_A');
要获取当前数据库名称,也可以使用SQL
DB::select('SELECT DATABASE()')[0]->{"DATABASE()"} // 如果您不使用MySQL,DATABASE()可能是其他内容
更新 - 表前缀
使表前缀也具有动态性的一种简单方法是在您的BaseModel构造函数中添加以下内容
$this->setTable('custName'.$this->getTable());
太棒了,我认为这是缺失的95%。我真的很想接受你的答案,但似乎还有一个问题 🙁 在DB::reconnect之后,表前缀似乎被忽略了。有什么想法吗?我之前没有提到这一点,因为我认为这是无关紧要的,但我的一些表具有前缀,而其他表则没有,我需要能够在运行时设置这些前缀...
嗯...也许在重新连接后尝试DB::setDefaultConnection('customers');
。 (不知道为什么,但我在一个在线论坛上找到了这个方法)。如果你使用USE db_A
方法呢?
setDefaultConnection没有起作用:(我将尝试使用USE db_A方法,但我想知道它是否会保留前缀...稍后我会尝试并更新话题。
它是相同的,前缀被忽略了...目前唯一的解决方法是在进入BaseModel构造函数之前引入前缀。这不是我希望的那么干净,但除非有其他建议,否则这很可能是我最终要做的...
奇怪...我刚刚试了一下,一切都正常。您在数据库配置中定义了前缀,对吗?
不完全是。由于每个客户都有一个前缀,如果我们把所有内容都放在数据库配置中,那将是丑陋的。前缀在数据库配置中是空的,但我通过BaseModel中的代码进行了设置(出于简单起见,我没有放在这里,但将更新问题)。总结一下:我的前缀配置为空,但我通过BaseModel中的代码进行了设置(但不起作用)...
我明白了...我不知道为什么在更改数据库时会忽略它,但我在我的答案中更新了一个解决该问题的方法的解释。
我没有足够的话来感谢你:) setTable拯救了这一天!
对于任何寻找相同问题的人,我通过执行DB::purge(DB::getDefaultConnection()); DB::Reconnect($new_connection);解决了前缀问题。