0

Replicated models and multi-database Eloquent

Advanced5 min read·lv-12-025
sql

Concept

Multi-database Eloquent enables connecting different models to different database connections, essential for applications that span multiple databases (read replicas, separate product databases, legacy systems).

Multiple connections: Define connections in config/database.php under the connections array. Each has its own driver, host, database, username, password.

Model-level connection: protected $connection = 'mysql_legacy'. All queries for this model use the named connection.

Model::on('connection'): Run a single query on a specific connection without changing the model's default: User::on('mysql_read')->where('active', true)->get().

DB::connection('name'): Use the Query Builder on a specific connection.

Read/write splitting: The mysql connection in database.php supports read and write sub-arrays. read is an array of servers — Eloquent selects reads (SELECT) from read servers and writes (INSERT/UPDATE/DELETE) to the write server automatically.

Sticky reads: After a write, subsequent reads in the same request go to the write server (to avoid replica lag). Configured with 'sticky' => true in the connection.

Cross-database joins: Only possible between databases on the same server (use database.table notation). Not possible with the Query Builder across different DB hosts.

Code Example

php
<?php
// config/database.php — multiple connections
'connections' => [
    'mysql' => [
        'driver'   => 'mysql',
        'host'     => env('DB_HOST', '127.0.0.1'),
        'database' => env('DB_DATABASE'),
        // read/write splitting:
        'read' => [
            ['host' => env('DB_READ_HOST_1', '127.0.0.1')],
            ['host' => env('DB_READ_HOST_2', '127.0.0.1')],
        ],
        'write' => [
            ['host' => env('DB_HOST', '127.0.0.1')],
        ],
        'sticky' => true, // reads after a write go to the write server
    ],
    'analytics' => [
        'driver'   => 'mysql',
        'host'     => env('ANALYTICS_DB_HOST'),
        'database' => env('ANALYTICS_DB_DATABASE'),
        'username' => env('ANALYTICS_DB_USER'),
        'password' => env('ANALYTICS_DB_PASS'),
    ],
    'legacy' => [
        'driver'   => 'mysql',
        'host'     => env('LEGACY_DB_HOST'),
        'database' => env('LEGACY_DB_DATABASE'),
        'username' => env('LEGACY_DB_USER'),
        'password' => env('LEGACY_DB_PASS'),
    ],
],

// Model bound to specific connection
namespace App\Models;

class EventLog extends \Illuminate\Database\Eloquent\Model
{
    protected $connection = 'analytics';
    protected $table      = 'event_logs';
    public $timestamps    = false;
}

class LegacyCustomer extends \Illuminate\Database\Eloquent\Model
{
    protected $connection = 'legacy';
    protected $table      = 'wp_users';
    protected $primaryKey = 'ID';
}

// Per-query connection override
$users = User::on('analytics')->where('cohort', '2024')->get();

// Query Builder on named connection
DB::connection('analytics')
    ->table('page_views')
    ->where('date', today())
    ->count();

// Transactions on specific connections
DB::connection('legacy')->transaction(function() {
    // operations on legacy DB in a transaction
});

// Cross-DB on same server (different DB, same host)
DB::table('orders')
    ->join('analytics.page_views as pv', 'orders.session_id', '=', 'pv.session_id')
    ->select('orders.id', 'pv.referrer')
    ->get();