我正在制作一个多租户多数据库应用程序,它有一个中央数据库和许多子数据库。
该应用程序在中央数据库中创建一个组织实例,并为每个组织创建一个包含不同表的子数据库。
为实现这一目标,我创建了一个类Setup
所有这些都包含在构造函数中,因此在调用Setup::create
时,所有这些都可以正常运行。
大多数数据库配置和连接都来自tutorial。
为了测试我的逻辑是否符合要求,我通过以下方式与我的应用程序进行了交互:
令我惊讶的是,结果在两种情况下都是不同的,并且就连接到另一个数据库而言从来没有想过。
与修补程序的交互:
创建调用我的Setup::create
并输出告诉我一切正常后,我会尝试自己查看我现在正在使用的数据库使用:
DB::connection()->getDatabaseName()
它输出我们刚刚为组织创建并连接到的子数据库名称,这是合乎逻辑的,也是相应的。
但是,我尝试通过为它创建一个新配置来连接到另一个数据库,然后使用我提供的DB
方法连接到它,它不起作用,我仍在子数据库中好了。
与浏览器互动:
这一次,我的Setup::create
在我的控制器代码中正确包裹,我尝试再次测试所有内容,我也在我的布局中输出一行来输出当前数据库:
<?php echo DB::connection()->getDatabaseName() ?>
首先,当我仍在中央数据库时,会显示其名称,但是在调用Setup::create
后,它会切换到子数据库 - 预期 - 但是,一次刷新后,我再次访问中央数据库 - 这是完全意外的 -
那么,这里发生了什么?以及如何在我希望的时候连接到我所有不同的数据库?
附加:
在修补程序中进行测试,我已经到了我已经注释掉迁移代码的地步,并且离开了数据库的创建以及与它的连接。 令我惊讶的是,它没有连接到数据库。 所以我开始认为迁移代码与连接数据库有关,或者修补程序有不同的行为我完全进入。
重要:
Model::create
而不是DB::connection()->....
技术细节:
我在Ubuntu Machine上使用带有mysql-server的Laravel 5。
答案 0 :(得分:2)
我偶然发现了这个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;
}
}
所以,每当我在一个操纵子数据库表的控制器中时,我就是这样:
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
或类似的任何错误,请务必查看我提供的问题链接,并正确使用use
语句。
关于即将到来的答案:
在我看来,这个数据库连接位于控制器操作的括号内,因此当我继续执行另一个指定无连接的操作时,它会自动返回到中央数据库。
让我觉得必须有一种方法可以以“全局”的方式将数据库连接设置为子数据库,以实现整个功能,例如中间件或其他东西。
我很乐意看到答案,实现这样的事情。
更新:
我想出了一个更简洁的方法来做到这一点。
我认为你和我在同一个地方,希望根据每个控制器有条件地更改数据库......比如,每个控制器都需要一个不同的数据库,只是为了争论。
我们将用来解决这个问题的是`Middlewares。
首先,解释我们将要做的事情。
我们将检查控制器的名称(甚至是操作),然后设置我们希望设置的正确数据库。
转到命令行,输入:
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];
}
这将返回一个包含动作名称和控制器名称的数组,如果您想限制性地返回控制器的名称,请随意从代码中删除'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.现在,您已经正确创建了中间件,让我们告诉您的应用程序在哪里找到它以及以什么名称。
在$routeMiddleware
变量中,添加此行
'set_proper_database' => \App\Http\Middleware\SetProperDatabase::class,
这样我们就知道如何调用它。
最后,设置它。
Controller.php
(所有控制器继承的抽象类) public function __construct()
{
$this->middleware('set_proper_database');
}
这应该为你做。
如果您有任何其他问题,请随时发表评论。
//资源:
3。Further Middleware Documentation 备注: 我很欣赏有关我的样式和代码缩进的版本,因为我似乎很难在这里正确地设计我的代码,但是徒劳无功,我使用的缩进没有任何效果。