如何在 运行 时间连接到不同的数据库?
How do I connect to different databases at run time?
我正在制作一个多租户多数据库应用程序,它有一个中央数据库和许多子数据库。
该应用程序在中央数据库中创建一个组织实例,并为每个组织创建一个包含不同表的子数据库。
为了实现这一点,我制作了一个 class class Setup
- 创建组织
- 创建其数据库
- 配置与该数据库的连接并连接到它
- 运行正确的迁移到它。
所有内容都包含在构造函数中,因此在调用 Setup::create
时所有这些都可以正常运行。
大部分数据库配置和连接都受此启发tutorial。
为了测试我的逻辑是否符合要求,我通过以下方式与我的应用程序交互:
- 修补匠
- Web 浏览器。
令我惊讶的是,这两种情况下的结果都不同,而且就连接到另一个数据库而言从来没有像我们想要的那样。
与 tinker 的互动:
在创建调用我的 Setup::create
并且输出告诉我一切正常后,我尝试自己检查我现在使用的是哪个数据库使用:
DB::connection()->getDatabaseName()
它输出我们刚刚为组织创建并连接到的子数据库名称,这是合乎逻辑的并相应地进行。
但是,我尝试通过为它创建一个新配置然后使用我提供的 DB
方法连接到它来连接到另一个数据库,它不起作用,我仍然在子-我所在的数据库。
与浏览器交互 :
这一次,将我的 Setup::create
正确包装在我的控制器代码中,我尝试再次测试所有内容,我还在我的布局中制作了一行以输出当前数据库:
<?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,它有我的答案。
我做了一个 class 叫做 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;
}
}
所以,每当我在一个操作子数据库表的控制器中时,我只是这样做:
public function RandomActionInMyController()
{
$db_connection = new DatabaseConnection(['database' => 'name_of_db']);
$someModel = new Model/Model::find()..// Basically anything
return myreturnstuff;
}
额外奖金:
静态属性$instances
在我的DatabaseConnection
中的使用
归结为检索我最新的数据库连接以方便使用。
例如,如果我想检索它,它将被包装在一个函数中,例如
function CurrentOrLatestDbConnection()
{
if( !empty(DatabaseConnection::$instances) )
{
return end(DatabaseConnection::$instances)->getConnection()->getDatabaseName();
}
}
注释 :
如果您遇到 Unknown class 'Container'
或 Capsule
之类的错误,请务必检查我提供的问题 link,然后使用 use
语句正确。
关于即将到来的答案 :
在我看来,此数据库连接存在于控制器操作的括号内,因此当我继续执行另一个指定无连接的操作时,它会自动 return 到中央数据库。
这让我想到,必须有一种方法可以通过'global'方式将数据库连接到子数据库设置为整个功能,例如中间件或其他东西。
我很想看到一个答案,实现这样的事情。
更新 :
我想出了一个更简洁的方法。
我假设你和我一样,想根据每个控制器有条件地改变数据库...比如说,你的每个控制器都需要一个不同的数据库,只是为了争论。
我们将使用`中间件来解决这个问题。
首先,解释一下我们要做什么..
我们将检查控制器的名称(甚至操作),然后设置我们希望设置的正确数据库。
转到您的命令行,输入:
php artisan make:middleware SetDatabaseConnectionMiddleware
创建带有现成样板的中间件。
或者,如果您喜欢困难的方式,请转到您的 app_name/app/Http/Middleware 并手动创建一个。
转到你的辅助方法文件(如果你已经有一个,如果没有,伙计做一个!)
function getControllerAndActionName()
{
$action = app('request')->route()->getAction();
$controller = class_basename($action['controller']);
list($controller, $action) = explode('@', $controller);
return ['action' => $action, 'controller' => $controller];
}
这将 return 给你一个包含动作名称和控制器名称的数组,如果你想 return 只限制控制器的名称,请随时从中删除 'action' => $action
代码。
- 在你的中间件内部,它看起来是这样的:
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.Now,您已经正确创建了中间件,让我们告诉您的应用在哪里可以找到它以及使用什么名称。
- 转到您的 app_name/app/Http/Kernel.php
在您的 $routeMiddleware
变量中,添加此行
'set_proper_database' => \App\Http\Middleware\SetProperDatabase::class,
这样我们就知道怎么调用了。
终于开始设置了。
- 转到您的
Controller.php
(所有控制器继承的摘要class)
public function __construct()
{
$this->middleware('set_proper_database');
}
这应该可以为您完成。
如果您有任何其他问题,请随时发表评论。
// 资源:
1.
3.Further Middleware Documentation
备注 :
我希望看到一些关于我的样式和代码缩进的版本,因为我似乎在这里努力正确设置我的代码样式但徒劳无功,我使用的缩进没有效果。
我正在制作一个多租户多数据库应用程序,它有一个中央数据库和许多子数据库。
该应用程序在中央数据库中创建一个组织实例,并为每个组织创建一个包含不同表的子数据库。
为了实现这一点,我制作了一个 class class Setup
- 创建组织
- 创建其数据库
- 配置与该数据库的连接并连接到它
- 运行正确的迁移到它。
所有内容都包含在构造函数中,因此在调用 Setup::create
时所有这些都可以正常运行。
大部分数据库配置和连接都受此启发tutorial。
为了测试我的逻辑是否符合要求,我通过以下方式与我的应用程序交互:
- 修补匠
- Web 浏览器。
令我惊讶的是,这两种情况下的结果都不同,而且就连接到另一个数据库而言从来没有像我们想要的那样。
与 tinker 的互动:
在创建调用我的 Setup::create
并且输出告诉我一切正常后,我尝试自己检查我现在使用的是哪个数据库使用:
DB::connection()->getDatabaseName()
它输出我们刚刚为组织创建并连接到的子数据库名称,这是合乎逻辑的并相应地进行。
但是,我尝试通过为它创建一个新配置然后使用我提供的 DB
方法连接到它来连接到另一个数据库,它不起作用,我仍然在子-我所在的数据库。
与浏览器交互 :
这一次,将我的 Setup::create
正确包装在我的控制器代码中,我尝试再次测试所有内容,我还在我的布局中制作了一行以输出当前数据库:
<?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,它有我的答案。
我做了一个 class 叫做 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;
}
}
所以,每当我在一个操作子数据库表的控制器中时,我只是这样做:
public function RandomActionInMyController()
{
$db_connection = new DatabaseConnection(['database' => 'name_of_db']);
$someModel = new Model/Model::find()..// Basically anything
return myreturnstuff;
}
额外奖金:
静态属性$instances
在我的DatabaseConnection
中的使用
归结为检索我最新的数据库连接以方便使用。
例如,如果我想检索它,它将被包装在一个函数中,例如
function CurrentOrLatestDbConnection()
{
if( !empty(DatabaseConnection::$instances) )
{
return end(DatabaseConnection::$instances)->getConnection()->getDatabaseName();
}
}
注释 :
如果您遇到 Unknown class 'Container'
或 Capsule
之类的错误,请务必检查我提供的问题 link,然后使用 use
语句正确。
关于即将到来的答案 :
在我看来,此数据库连接存在于控制器操作的括号内,因此当我继续执行另一个指定无连接的操作时,它会自动 return 到中央数据库。
这让我想到,必须有一种方法可以通过'global'方式将数据库连接到子数据库设置为整个功能,例如中间件或其他东西。
我很想看到一个答案,实现这样的事情。
更新 :
我想出了一个更简洁的方法。
我假设你和我一样,想根据每个控制器有条件地改变数据库...比如说,你的每个控制器都需要一个不同的数据库,只是为了争论。
我们将使用`中间件来解决这个问题。
首先,解释一下我们要做什么..
我们将检查控制器的名称(甚至操作),然后设置我们希望设置的正确数据库。
转到您的命令行,输入:
php artisan make:middleware SetDatabaseConnectionMiddleware
创建带有现成样板的中间件。
或者,如果您喜欢困难的方式,请转到您的 app_name/app/Http/Middleware 并手动创建一个。
转到你的辅助方法文件(如果你已经有一个,如果没有,伙计做一个!)
function getControllerAndActionName() { $action = app('request')->route()->getAction(); $controller = class_basename($action['controller']); list($controller, $action) = explode('@', $controller); return ['action' => $action, 'controller' => $controller]; }
这将 return 给你一个包含动作名称和控制器名称的数组,如果你想 return 只限制控制器的名称,请随时从中删除 'action' => $action
代码。
- 在你的中间件内部,它看起来是这样的:
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.Now,您已经正确创建了中间件,让我们告诉您的应用在哪里可以找到它以及使用什么名称。
- 转到您的 app_name/app/Http/Kernel.php
在您的
$routeMiddleware
变量中,添加此行'set_proper_database' => \App\Http\Middleware\SetProperDatabase::class,
这样我们就知道怎么调用了。
终于开始设置了。
- 转到您的
Controller.php
(所有控制器继承的摘要class)
public function __construct()
{
$this->middleware('set_proper_database');
}
- 转到您的
这应该可以为您完成。
如果您有任何其他问题,请随时发表评论。
// 资源:
1.
3.Further Middleware Documentation 备注 : 我希望看到一些关于我的样式和代码缩进的版本,因为我似乎在这里努力正确设置我的代码样式但徒劳无功,我使用的缩进没有效果。