Change DB connection on the fly

Amazing solution:
(But this won’t work for eloquents)

config(['database.default' => 'mysql-tw']);

Override getConnectionName() of model

<?php
namespace App\Models;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Config;
use Illuminate\Database\Eloquent\Model;

use Carbon\Carbon;

class ParentModel extends Model{
    
    protected $connection = 'mysql';
    protected $force_conn = '';

    public function __construct(){
        parent::__construct();

        $site = request()->input('site');
        //logg($site);
        if($site != false){
            $conn_config = Config::get('admin.site_connections');
            $conn = $conn_config[$site];
            $this->connection = $conn;
        }
        //logg($this->connection);
        //logg(DB::connection()->getDatabaseName());
        //logg(config('database.default'));
    }

    public function getConnectionName()
    {
        if(request()->is('admin/*') == true){
            if($this->force_conn == false){
                $conn_config = Config::get('admin.site_connections');
                //logg($conn_config);
                $site = 'global';
                if(isset($_COOKIE['admin_site']) == true && $_COOKIE['admin_site'] != false){
                    $site = $_COOKIE['admin_site'];
                }
                //logg($site);
                $conn = $conn_config[$site];
                $this->connection = $conn;
            }else{
                $this->connection = $this->force_conn;
            }
        }

        return $this->connection;
    }

    public function setConnection($name)
    {
        $this->force_conn = $name;
        $this->connection = $name;

        return $this;
    }

    public static function on($connection = null)
    {
        // First we will just create a fresh instance of this model, and then we can set the
        // connection on the model so that it is used for the queries we execute, as well
        // as being set on every relation we retrieve without a custom connection name.
        $instance = new static;

        $instance->setConnection($connection);

        return $instance->newQuery();
    }
}

 

Test function

static function test(){
	//config(['database.default' => 'mysql-tw']);
	
	//$data = ProductM::on('mysql-tw')->find(1218);
	//logg($data);

	$data = DB::connection('mysql-tw')
		->table(self::$stable)
		->where('id', 1218)
		->first();
	logg($data);

	logg(DB::connection()->getDatabaseName());
}

Global Middleware

namespace App\Http\Middleware;

use Illuminate\Support\Facades\Config;

use Closure;

class AdminDbSwitch
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        //logg($request->is('admin/*'));
        if($request->is('admin') || $request->is('admin/*')){
            $conn_config = Config::get('admin.site_connections');
            //logg($conn_config);
            $site = 'global';
            if(isset($_COOKIE['site']) == true && $_COOKIE['site'] != false){
                $site = $_COOKIE['site'];
            }
            //logg($site);
            $conn = $conn_config[$site];
            //logg($conn);
            config(['database.default' => $conn]);
        }

        return $next($request);
    }
}

app/Http/Kernel.php

The position you put the middleware in is critical. Since cookie is used in the middleware, you can’t put it in global middleware because cookie and session are not activated yet.

The correct position is in “web” group

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\AdminDbSwitch::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

https://laracasts.com/discuss/channels/laravel/session-doesnt-work-in-middleware

Reference:

  1. https://stackoverflow.com/questions/31041893/laravel-change-database-connection-run-time
  2. Override DB facade
    https://stackoverflow.com/questions/40614875/laravel-5-extend-a-facade/40615078#40615078
  3. Extending DB facade
    https://stackoverflow.com/questions/52944843/extending-db-facade-laravel
  4. Change database configs
    https://laracasts.com/discuss/channels/eloquent/how-to-modify-database-connection-string-in-laravel-5
  5. Extend model (For eloquent)
    https://stackoverflow.com/questions/28985472/change-database-connection-in-laravel-model