问题描述
我正在制作一个拥有一个中央数据库和许多子数据库的多租户多数据库应用程序。
应用程序在中心数据库,并且为每个组织创建了具有不同表的子数据库。
为了实现这一点,我创建了一个类类 / code>
- 创建组织
- 创建其数据库
- 配置与该数据库的连接并连接到该数据库。
- 运行适当的迁移。
所有包装在一个构造函数中,所以当 Setup :: create
时,所有这些都会正常运行。
大多数数据库配置和连接都来自此。
为了测试我的逻辑是否如预期,我通过以下方式与我的应用程序交互:
- Tinker
- 网络浏览器。
b $ b
对我来说,结果在这两种情况下是不同的,而且就连接到另一个数据库而言,结果是不同的。
与tinker的互动:
创建调用我的 Setup :: create
好的,我试着检查我现在的数据库我现在使用:
DB :: connection() ; getDatabaseName()
它输出我们刚刚为组织创建的子数据库名称,并连接到,这是逻辑的,并相应地。
但是,我试图连接到另一个数据库,创建一个新的配置,然后连接到它与 DB
我提供的方法,它不工作,我仍然在我的子数据库。
与浏览器互动:
这次,让我的
在我的控制器代码中正确地包装,我试图再次测试一下,我也在我的布局中输出一行,以输出当前数据库:
<?php echo DB :: connection() - > getDatabaseName()?>
首先,虽然我仍在中央数据库,但是在调用 Setup :: create
,它切换到子数据库 - 这是预期的 - 但是,然后,一次刷新后,我再次在中央数据库 - 完全意外 -
那么,这里会发生什么?
额外:
重要:
:- 我遇到过使用QueryBuilders的解决方案>
- 请不要提供这样的答案,因为我的目标是将数据库完全切换到我可以使用雄辩模型的事件没有问题的点。
- 我的意思是,我想在连接之后能够使用
Model :: create
到数据库而不是DB :: connection() - > ....
- 我的意思是,我想在连接之后能够使用
技术详情:
我使用Laravel 5与mysql- ,在Ubuntu机器上。
我偶然发现了,它有我的回答。
$ c> DatabaseConnection :
class DatabaseConnection extends Model
{
static $ instances = array();
protected $ database;
protected $ connection;
public function __construct($ options = null)
{
//设置数据库
$ database = $ options ['database'];
$ this-> database = $ database;
//找出驱动程序并获取驱动程序的默认配置
$ driver = isset($ options ['driver'])? $ options ['driver']:Config :: get(database.default);
$ default = Config :: get(database.connections。$ driver);
//循环遍历我们的默认数组和更新选项,如果我们有非默认值
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();
//创建连接
$ 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()..//基本上任何东西
return myreturnstuff;
}
额外奖金:
在我的 DatabaseConnection
中使用静态属性 $ instances
例如,如果我想检索它,它将被包装在一个函数中,例如
function CurrentOrLatestDbConnection()
{
if(!empty(DatabaseConnection :: $ instances))
{
return end(DatabaseConnection :: $ instances) - > getConnection() - > getDatabaseName();
}
}
p>
如果您遇到错误,例如未知类'Container'
或 Capsule
或任何类型的,请确保您检查我提供的问题链接,并使用正确使用
语句。
关于即将到来的答案:
在我看来,此数据库连接位于控制器操作的括号内,继续执行另一个操作,指定没有连接,它会自动返回到中央数据库。
这让我想到必须有一种方法设置数据库连接到
我希望看到一个答案,实现这样的东西。
p>更新:
我想出了一个更简单的方法。 p>
我假设你和我在同一个地面上,希望根据每个控制器有条件地更改数据库...说,每个控制器需要一个不同的数据库,只是为了
首先,我们将使用解释我们要做什么。
我们要检查控制器的名称(甚至动作),然后设置我们希望的正确的数据库
p>
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'=>
- 在您的中间件中, :
命名空间App\Http\\ \\中间件;
使用Closure;
使用DatabaseConnection;
类SetProperDatabase
{
/ **
*处理传入请求。
*
* @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.现在, ,
- 转到您的app_name / app / Http / Kernel.php
-
在
$ routeMiddleware
变量中,添加此行
'set_proper_database'=> \App\Http\Middleware\SetProperDatabase :: class,
-
最后,设置它。 b
$ b- 转到您的
Controller.php
(所有控制器继承的Abstract类) li>
public function __construct()
{
$ this-> middleware('set_proper_database');
}
- 转到您的
如果您有任何其他问题,请随意发表评论。
//资源:
1。
2。
注释:
我会感谢一些关于我的样式和代码缩进的版本,因为它似乎很难正确地在这里,但是徒劳,我使用的缩进没有effeft。
I am making a multi-tenant multi-database app that has one central database and many sub-databases.
The app creates an instance of an organisation in the central database, and to each organisation it creates a sub-database with different tables.
To achieve this, I have made a class class Setup
that
- Creates the Organisation
- Creates its Database
- Configures the connection to that database and connects to it
- Runs the proper migrations to it.
All wrapped up in a constructor, so upon caling Setup::create
all of this runs properly.
Most of the database configuration and connection are inspiried from this tutorial.
To test whether my logic goes as wanted, I interacted with my application via :
- Tinker
- Web Browser.
To my suprise, the outcome is different in both cases, and never as wanted as far as connecting to another database is concerned.
Interaction with tinker :
After creating calling my Setup::create
and having output telling me everything went okay, I try to check for myself what database am I on right now Using:
DB::connection()->getDatabaseName()
It outputs the sub-database name we have just created for the organisation and connected to, which is logical and going accordingly.
However, I attempt to connect to another database by creating a new configuration for it and then connecting to it with the DB
methods I have provided, it does not work, I am still on the sub-database I was on.
Interacting with the browser :
This time, having my Setup::create
wrapped up properly in my controller's code, I attempt to test everything again, I also made a line in my layout to output me the current database :
<?php echo DB::connection()->getDatabaseName() ?>
At first, while I am still on the central database, its name appears, however after calling Setup::create
, it switches to the sub-database -Which is expected- but then, after one refresh, I am on the central database again -Which is totally Unexpected-
So, what happens here? and how do I get to connect to all of my different databases how I wish when I wish?
Extra:
Important:
- I have came across threads where solutions using QueryBuilders were mentioned
- Please, do not provide such answers because my aim is to switch databases entirely to the point where I can use eloquent model's events with no problem.
- By that I mean, I want to be able to use
Model::create
after having connected to the database instead ofDB::connection()->....
- By that I mean, I want to be able to use
Technical details:
I am using Laravel 5 with mysql-server, on Ubuntu Machine.
I stumbled upon this question and it had my answer.
I made a class called 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;
}
}
So, whenever I am in a controller that manipulates tables of a sub-database, I simply go this way:
public function RandomActionInMyController()
{
$db_connection = new DatabaseConnection(['database' => 'name_of_db']);
$someModel = new Model/Model::find()..// Basically anything
return myreturnstuff;
}
Extra Bonus:
The use of the static attribute $instances
in my DatabaseConnection
boils down to retrieving my latest database connection for ease uses.
For example, if I ever wanted to retrieve it, it would be wrapped in a function such as
function CurrentOrLatestDbConnection()
{
if( !empty(DatabaseConnection::$instances) )
{
return end(DatabaseConnection::$instances)->getConnection()->getDatabaseName();
}
}
Notes :
If you encounter errors such as Unknown class 'Container'
or Capsule
or anything of that kind, make sure you check the question link I have provided, and use use
statements properly.
Concerning upcoming answers :
It seems to me that this database connection lives within the the brackets of the controller's action, so when I proceed to another action that specifies no connection, it returns to the central database automatically.
Which has got me thinking that there must be a way to set the database connection to the sub-database in a 'global' way to the whole function, such as a middleware or something.
I would love to see an answer, implementing such thing.
Update :
I came up with a neater way to do it.
I assume you are on the same ground as me, wanting to change databases conditionally in accordance with each controller... say, each of your controllers requires a different database, just for the sake of the argument.
What we will be using to solve this is `Middlewares.
First, to explain what we are about to do..
We are going to check for the name of the controller (and even action) and then set the proper database we wish to set.
Go to your command-line , type in:
php artisan make:middleware SetDatabaseConnectionMiddleware
To create a middleware with ready boilerplate.
Or, if you like it the hard way, go to your app_name/app/Http/Middleware and create one manually.
Go to your helper methods file( if you already have one, if not, dude make one!)
function getControllerAndActionName() { $action = app('request')->route()->getAction(); $controller = class_basename($action['controller']); list($controller, $action) = explode('@', $controller); return ['action' => $action, 'controller' => $controller]; }
This will return to you an array with both the action name and controller name, if you want to return restrictidly just the controller's name, feel free to remove 'action' => $action
from the code.
- Inside of your middleware, it will look this way :
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, that you have created properly your middleware, let us tell your app where to find it and under what name.
- Go to your app_name/app/Http/Kernel.php
In your
$routeMiddleware
variable, add this line'set_proper_database' => \App\Http\Middleware\SetProperDatabase::class,
This way we know how to call it.
Finally, setting it up.
- Go to your
Controller.php
(the Abstract class from which all of your controller's inherit)
public function __construct()
{
$this->middleware('set_proper_database');
}
- Go to your
And this should do it for you.
If you have any further questions, please feel free to comment.
// Resources :
3.Further Middleware DocumentationNotes :I'd appreciate some edition concerning my styling and code indenting, since it seems I struggled to style my code properly in here but in vain, the indentions I used had no effeft.
这篇关于如何在运行时连接到不同的数据库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!