如何在Laravel 5中动态更改/解决数据库连接?

5
我正在开发一个多租户多数据库架构的应用程序,这意味着每个租户都有自己的数据库,而不是所有租户都存在于同一个数据库中。
现在,我一直在努力解决无法成功更改数据库连接的问题,我也不确定正确的方法是什么。
我的数据库配置文件如下:
'connections' => [

    'archive' => [
        'driver'    => 'mysql',
        'host'      => env('DB_HOST', 'localhost'),
        'database'  => env('DB_DATABASE', 'forge'),
        'username'  => env('DB_USERNAME', 'forge'),
        'password'  => env('DB_PASSWORD', ''),
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => '',
        'strict'    => false,
    ],

    'tenant' => [
        'driver'    => 'mysql',
        'host'      => env('DB_HOST', 'localhost'),
        'database'  => env('DB_DATABASE', 'forge'),
        'username'  => env('DB_USERNAME', 'forge'),
        'password'  => env('DB_PASSWORD', ''),
        'charset'   => 'utf8',
        'collation' => 'utf8_unicode_ci',
        'prefix'    => '',
        'strict'    => false,
    ],

],

基本上,主数据库是存档,它保存了所有租户(公司)的信息。当用户想要登录时,他首先提供租户(公司)用户名,然后提供自己的登录凭据。这样我就知道应该连接哪个数据库并认证用户。一旦认证成功,我将租户的用户名保存到会话username变量中,这又被我编写的中间件使用。所以,如果会话有一个username变量,我就知道每次请求来自哪个用户并切换到相应的数据库。这样我就可以跟踪每个请求应该连接到哪个数据库。
登录逻辑:
$this->changeDb($request->input('username'));
$this->saveUsernameToSession($request);

if (\Auth::attempt(['email' => $request->input('email'),
                 'password' => $request->input('password')])) {
    // Authentication passed...
    return redirect('/');
}else{
    // If the auth atttemp is not successful
    // Set default DB as the main one again.
    $request->session()->pull('username');
}

现在我感兴趣的是changeDB()方法,但不确定自己是否正确地使用了它。
\Config::set('database.connections.tenant.database', $username);
\Config::set('database.default', 'tenant');

这是它在幕后的操作。它基本上会将数据库配置文件中的租户连接更改为租户的用户名,因为它与租户的数据库相同。然后它将默认数据库设置为租户连接。
这是解决DB连接的正确方法吗?还是我不知道其他方法。也许Laravel在内部提供了一些方法,我可以用它来切换/解决数据库连接问题。
登录功能正常工作,但我担心的原因是其他许多事情并不好使,例如,当我尝试种植假租户和他们的假用户时,我遇到了这个问题。 Seeder runs fine the first time but does not do some tasks in the subsequent loops in Laravel 5? 我正在使用与登录相同的方法,但它却没有起作用。虽然提供的答案解决了我的问题,但我仍然不确定如何解决?
此外,我试图在控制器方法中使用类似的方法来更改数据库,但它并没有像我预期的那样工作。
public function showUsersForTenant($id)
{
    $tenant = Tenant::findOrFail($id);

    \Config::set('database.connections.tenants.database', $tenant->username);

    $users = User::on('tenant')->get();

    return response()->json($users, 200);
}

这应该返回特定租户(公司)的所有用户给连接到主数据库的系统超级管理员。但它没有连接到租户数据库,而是一直从主DB返回用户。
有一些软件包支持多租户多数据库方法,但由于我是初学者开发人员,我不能从它们的存储库中得出太多有意义的东西,也不知道它们如何实时更改/解决DB连接。

https://github.com/orchestral/tenanti

https://github.com/laraflock/multi-tenant

https://github.com/uxweb/laravel-multi-db

那么如何在Laravel 5中成功更改/切换/解析连接?


DB::connection('archive')->select()->where()->get()->... DB::connection('tenant')->select()->where()->get()->...DB::connection() 接受一个参数作为配置文件中连接的名称。空参数返回默认连接。 - Silwerclaw
对不起,我不理解。 - Rohan
你的配置文件中保存了2个或更多连接->使用方法DB :: connection('your-connection-name-here')将自动选择您提供的连接。然后,使用链接,您可以为此连接编写任何查询生成器。例如,DB :: connection('archive')-> select('id')-> where('id','>',2)-> get()将在您的“存档”连接上运行此查询。 - Silwerclaw
此外,如果您使用Eloquent模型->每个模型都可以有属性protected $connection;告诉Laravel在使用此模型时要使用哪个连接。 - Silwerclaw
好的,假设我在Eloquent模型中设置了与租户的连接。然后,在请求之前,我只需更改配置中的数据库名称。但是当我需要使用另一个数据库连接时,我该怎么办?我不想编写查询构建器。这很难解释。 - Rohan
1个回答

5
作为您提到的第二个包(hyn/multi-tenant)的作者,我可以告诉您秘密就在于您在Laravel中挂钩的位置。
假设您正在使用原始的Laravel版本,则Model选择其连接的位置是从getConnection()方法中进行的。通过覆盖Eloquent Model并使所有应用程序模型使用该扩展模型,您可以定义“即时”使用哪个连接。
现在来谈谈第二个问题,涉及到种子和迁移。这是一个完全不同的故事。我实际上否决了Laravel默认提供的MigrateCommand,以便保持与Laravel的工作方式尽可能接近。更简单的方法是创建一个自定义命令,设置特定连接的配置,然后在该命令中运行迁移/种子命令并带有连接参数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接