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

Php 如何在运行时连接到不同的数据库?,php,mysql,database,laravel-5,multi-tenant,Php,Mysql,Database,Laravel 5,Multi Tenant,我正在制作一个多租户多数据库应用程序,它有一个中央数据库和多个子数据库 该应用程序在中央数据库中创建一个组织的实例,并为每个组织创建一个包含不同表的子数据库 为了实现这一点,我制作了一个classSetup 创建组织 创建其数据库 配置与该数据库的连接并连接到该数据库 运行到它的正确迁移 所有这些都封装在一个构造函数中,因此在校准Setup::create时,所有这些都可以正常运行 大多数数据库配置和连接都是从中得到启发的 为了测试我的逻辑是否符合要求,我通过以下方式与我的应用程序交互: 修补匠

我正在制作一个多租户多数据库应用程序,它有一个中央数据库和多个子数据库

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

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

  • 创建组织
  • 创建其数据库
  • 配置与该数据库的连接并连接到该数据库
  • 运行到它的正确迁移
  • 所有这些都封装在一个构造函数中,因此在校准
    Setup::create时,所有这些都可以正常运行

    大多数数据库配置和连接都是从中得到启发的

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

  • 修补匠
  • 网络浏览器
  • 令我惊讶的是,在这两种情况下,结果都不一样,而且就连接到另一个数据库而言,结果从来都不是人们想要的

    与tinker的互动

    创建并调用我的
    设置::create
    并让输出告诉我一切正常后,我尝试自己检查我现在使用的是什么数据库:

    DB::connection()->getDatabaseName()
    
    它输出我们刚刚为组织创建并连接到的子数据库名称,这是符合逻辑的,并且是相应的

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


    与浏览器交互

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

    <?php echo DB::connection()->getDatabaseName() ?>
    
    
    
    起初,当我仍然在中央数据库上时,它的名称出现了,但是在调用
    Setup::create
    后,它切换到子数据库-这是预期的-但是,在一次刷新后,我又在中央数据库上了-这完全出乎意料-

    那么,这里发生了什么?我怎样才能连接到我所有的不同数据库?我希望什么时候我希望什么

    额外:

    在tinker中进行测试时,我已经注释掉了迁移代码,留下了数据库的创建以及与数据库的连接。 令我惊讶的是,它没有连接到数据库。 所以我开始认为迁移代码与连接到数据库有关,或者tinker有我完全关注的不同行为

    重要:

    • 我遇到过提到使用QueryBuilder的解决方案的线程
    • 请不要提供这样的答案,因为我的目标是完全切换数据库,这样我就可以毫无问题地使用雄辩模型的事件。
      • 我的意思是,在连接到数据库之后,我希望能够使用
        Model::create
        ,而不是
        DB::connection()->..
    技术细节

    我正在Ubuntu机器上使用Laravel5和mysql服务器。

    我偶然发现了这一点,它给出了我的答案

    我创建了一个名为
    数据库连接
    的类:

    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
    归结起来就是检索我的最新数据库连接以便于使用

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

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

    如果遇到诸如
    未知类“容器”
    胶囊
    之类的错误,请确保检查我提供的问题链接,并正确使用
    使用
    语句

    关于即将到来的答案

    在我看来,这个数据库连接位于控制器操作的括号内,因此当我继续执行另一个未指定连接的操作时,它会自动返回到中央数据库

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

    我希望看到一个答案,实现这样的事情

    更新

    我想出了一个更整洁的方法

    我假设你和我站在同一个立场上,希望根据每个控制器有条件地更改数据库。。。比如说,每个控制器都需要一个不同的数据库,只是为了参数

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

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

    我们将检查控制器的名称(甚至操作),然后设置我们希望设置的适当数据库

  • 转到命令行,键入:

    php artisan make:中间件SetDatabaseConnectionMiddleware

  • 使用现成的样板创建中间件

    或者,如果您非常喜欢,请转到您的app_name/app/Http/Middleware并手动创建一个

  • 转到你的助手方法文件(如果你已经有了一个,如果没有,就做一个!)

  • 这将向您返回一个包含操作名和控制器名的数组,如果您只想限制性地返回控制器名,请随意从代码中删除
    'action'=>$action

  • 在中间件内部,它的外观如下:
  • 4.现在,您已经正确创建了中间件,让我们告诉您的应用程序在哪里可以找到它以及它的名称

  • 转到你的app_name/app/Http/Kernel.php
  • $routeMiddleware
    变量中,添加此行

    “设置正确的数据”
    
     function getControllerAndActionName()
    {
    
    $action = app('request')->route()->getAction();
    
    $controller = class_basename($action['controller']);
    
    list($controller, $action) = explode('@', $controller);
    
    return ['action' => $action, 'controller' => $controller];
    }
    
        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);
        }
        }