gpt4 book ai didi

php - 如何在运行时连接到不同的数据库?

转载 作者:搜寻专家 更新时间:2023-10-30 23:02:05 24 4
gpt4 key购买 nike

我正在制作一个 Multi-Tenancy 多数据库应用程序,它有一个中央数据库和许多子数据库。

该应用程序在中央数据库中创建一个组织实例,并为每个组织创建一个包含不同表的子数据库。

为了实现这一点,我制作了一个类 Setup

  1. 创建组织
  2. 创建数据库
  3. 配置与该数据库的连接并连接到它
  4. 对其进行适当的迁移。

所有这些都包含在构造函数中,因此在调用 Setup::create 时,所有这些都可以正常运行。

大部分数据库配置和连接都是从这个tutorial中得到启发的.

为了测试我的逻辑是否符合要求,我通过以下方式与我的应用程序交互:

  1. 修补匠
  2. 网络浏览器。

令我惊讶的是,这两种情况下的结果都不同,而且就连接到另一个数据库而言从来都不是我们想要的。

与 tinker 的互动:

在调用我的 Setup::create 并且输出告诉我一切正常后,我尝试自己检查我现在使用的是哪个数据库使用:

DB::connection()->getDatabaseName()

它输出我们刚刚为组织创建并连接到的子数据库名称,这是合乎逻辑的,并且是相应的。

但是,我尝试通过为它创建一个新配置然后使用我提供的 DB 方法连接到它来连接到另一个数据库,它不起作用,我仍然在 sub -我所在的数据库。


与浏览器交互:

这一次,将我的 Setup::create 正确包装在我的 Controller 代码中,我尝试再次测试所有内容,我还在我的布局中制作了一行以输出当前数据库:

<?php echo DB::connection()->getDatabaseName() ?>

起初,当我还在中央数据库时,它的名字出现了,但是在调用 Setup::create 之后,它切换到子数据库 -这是预期的- 但是,刷新一次后,我又进入了中央数据库 -这完全出乎意料-

那么,这里发生了什么?以及如何在需要时以我希望的方式连接到所有不同的数据库?

额外:

Testing in tinker, I have went to the point where I have commented out the migration code, and left the creation of the database and also the connection to it. To my suprise, it does not connect to the database. so I started thinking that the migration code has something to do with connecting to the database, or maybe tinker has different behaviors I completely ingore.

重要:

  • 我遇到过一些线程,其中提到了使用 QueryBuilder 的解决方案
  • 请不要提供此类答案,因为我的目标是将数据库完全切换到可以毫无问题地使用 Eloquent 模型事件的程度。
    • 我的意思是,我希望在连接到数据库之后能够使用 Model::create 而不是 DB::connection()->....

技术细节:

我在 Ubuntu 机器上使用 Laravel 5 和 mysql-server。

最佳答案

我偶然发现了这个 question它有我的答案。

我创建了一个名为 DatabaseConnection 的类:

class DatabaseConnection extends Model
{

static $instances=array();

protected $database;

protected $connection;

public function __construct($options = null)
{
// Set the database
$database = $options['database'];
$this->database = $database;

// Figure out the driver and get the default configuration for the driver
$driver = isset($options['driver']) ? $options['driver'] : Config::get("database.default");
$default = Config::get("database.connections.$driver");

// Loop through our default array and update options if we have non-defaults
foreach($default as $item => $value)
{
$default[$item] = isset($options[$item]) ? $options[$item] : $default[$item];
}

$capsule = new Capsule;
$capsule->addConnection($default);
$capsule->setEventDispatcher(new Dispatcher(new Container));
$capsule->setAsGlobal();
$capsule->bootEloquent();

// Create the connection
$this->connection = $capsule->getConnection();

DatabaseConnection::$instances[] = $capsule;
return $this->connection;
}
}

所以,每当我在一个 Controller 中操作一个子数据库的表时,我只需这样做:

public function RandomActionInMyController()
{
$db_connection = new DatabaseConnection(['database' => 'name_of_db']);
$someModel = new Model/Model::find()..// Basically anything
return myreturnstuff;
}

额外奖励:

在我的 DatabaseConnection 中使用静态属性 $instances归结为检索我最新的数据库连接以便于使用。

例如,如果我想检索它,它将被包装在一个函数中,例如

function CurrentOrLatestDbConnection()
{
if( !empty(DatabaseConnection::$instances) )
{
return end(DatabaseConnection::$instances)->getConnection()->getDatabaseName();
}
}

注释:

如果您遇到诸如 Unknown class 'Container'Capsule 之类的错误,请务必检查我提供的问题链接,并使用 正确使用语句。

关于即将到来的答案:

在我看来,这个数据库连接存在于 Controller 操作的括号内,所以当我继续执行另一个指定没有连接的操作时,它会自动返回到中央数据库。

这让我想到,必须有一种方法可以以“全局”方式将数据库连接设置为整个功能的子数据库,例如中间件或其他东西。

我很想看到一个答案,实现这样的事情。

更新:

我想出了一个更简洁的方法。

我假设您和我站在同一个立场上,想要根据每个 Controller 有条件地更改数据库...比如说,您的每个 Controller 都需要一个不同的数据库,只是为了争论。

我们将使用“中间件”来解决这个问题。

首先,解释一下我们要做什么..

我们将检查 Controller 的名称(甚至操作),然后设置我们希望设置的正确数据库。

  1. 转到您的命令行,输入:

    php artisan make:middleware SetDatabaseConnectionMiddleware

创建带有现成样板的中间件。

或者,如果您喜欢困难的方式,请转到您的 app_name/app/Http/Middleware 并手动创建一个。

  1. 转到你的辅助方法文件(如果你已经有一个,如果没有,老兄自己做一个!)

     function getControllerAndActionName()
    {

    $action = app('request')->route()->getAction();

    $controller = class_basename($action['controller']);

    list($controller, $action) = explode('@', $controller);

    return ['action' => $action, 'controller' => $controller];
    }

这将返回一个包含 Action 名称和 Controller 名称的数组,如果您只想返回 Controller 的名称,请随意删除 'action' => $action代码。

  1. 在你的中间件内部,它看起来是这样的:

    namespace App\Http\Middleware;

use Closure;
use DatabaseConnection;

class SetProperDatabase
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$database_name = '';
$controllerAndActionName = getControllerAndActionName();
$controller_name = $controllerAndActionName['controller'];
$action_name = $controllerAndActionName['action'];
if($controller_name == 'my_controller_nameController')
{

$database_name = 'your_proper_database_name';
}
else
{
$database_name = 'other_db';
}

$database_connection = new DatabaseConnection(['database' => $database_name']);

return $next($request);
}
}

4.现在,您已经正确地创建了您的中间件,让我们告诉您的应用在哪里可以找到它以及以什么名称找到它。

  1. 转到您的 app_name/app/Http/Kernel.php
  2. 在您的 $routeMiddleware 变量中,添加此行

    'set_proper_database' =>\App\Http\Middleware\SetProperDatabase::class,

这样我们就知道怎么调用了。

  1. 最后,进行设置。

    1. 转到您的Controller.php(您所有 Controller 的继承自的抽象类)

    公共(public)函数 __construct(){$this->middleware('set_proper_database');}

这应该可以为您完成。

如果您有任何其他问题,请随时发表评论。

//资源:

1. Controller And Action Name

2。 Middleware Documentation

3。 Further Middleware Documentation注意事项 :我会很感激关于我的样式和代码缩进的一些版本,因为我似乎在这里努力正确设置我的代码样式但徒劳无功,我使用的缩进没有效果。

关于php - 如何在运行时连接到不同的数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31281669/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com