Skip to content

Database

Core\Db\Adapter\Pdo\Sqlsrv / Dblib — adapter SQL Server custom (Phalcon inti tidak menyediakan). Konfigurasi lewat app/config/database.php:

php
return [
    'default' => 'main',
    'connections' => [
        'main' => [
            'adapter' => env('DB_ADAPTER', 'mysql'), // 'sqlsrv' | 'dblib' | 'mysql' | 'pgsql'
            'options' => [
                'host' => env('DB_HOST'),
                'port' => env('DB_PORT'),
                'dbname' => env('DB_NAME'),
                'username' => env('DB_USERNAME'),
                'password' => env('DB_PASSWORD'),
                'dialectClass' => str_replace('::class', '', env('DB_DIALECT', Phalcon\Db\Dialect\Mysql::class)),
                'TrustServerCertificate' => env('DB_TRUST_CERTIFICATE', 'true'),
                'LoginTimeout' => env_int('DB_LOGIN_TIMEOUT', 3),
            ],
        ],
        // koneksi tambahan otomatis terdaftar sebagai service 'db.{key}'
        'reporting' => [ 'adapter' => 'sqlsrv', 'options' => [...] ],
    ],
];

PdoFactory::load() Phalcon HANYA membaca field koneksi dari options (lihat contoh di atas) — field yang ditaruh flat langsung di bawah 'main' => [...] tanpa dibungkus 'options' diam-diam diabaikan, adapter gagal connect dengan pesan generik must be specified with either 'dbname' or 'dsn'. Core\Db\ConnectionConfig::forVariant() (dipakai internal oleh DefaultServiceProvider) punya fallback ke bentuk flat lama, tapi 'options' tetap bentuk yang direkomendasikan.

Koneksi tambahan diakses lewat facade:

php
use Core\Facades\DB;

DB::query(...);                        // koneksi default
DB::connection('reporting')->query(...); // koneksi lain

Read/write splitting (replica) ala Laravel

Tiap koneksi bisa punya config 'read' / 'write' / 'sticky' opsional — sub-array yang meng-override sebagian field 'options' (biasanya cuma 'host', boleh array multi-replica seperti Laravel, salah satu dipilih acak):

php
return [
    'default' => 'main',
    'connections' => [
        'main' => [
            'adapter' => 'mysql',
            'options' => [
                'dbname'   => env('DB_DATABASE', 'forge'),
                'username' => env('DB_USERNAME', 'forge'),
                'password' => env('DB_PASSWORD', ''),
                'charset'  => 'utf8mb4',
            ],
            'read' => [
                'host' => [env('DB_REPLICA_HOST', '127.0.0.1')],
            ],
            'write' => [
                'host' => [env('DB_HOST', '127.0.0.1')],
            ],
            'sticky' => true,
        ],
    ],
];

Kalau 'read' diisi, DefaultServiceProvider otomatis mendaftarkan service tambahan db.read (dan db.{key}.read untuk koneksi bernama), lalu meng-arahkan SEMUA Model ActiveRecord untuk baca lewat replica itu — tidak perlu setReadConnectionService() manual di tiap model. Ini jalan lewat mekanisme native Phalcon (Phalcon\Mvc\Model\Manager::setReadConnectionService() / setSticky()), bukan proxy custom, jadi otomatis kompatibel dengan semua fitur ORM (relasi, transaksi, dst).

  • Tanpa 'read' sama sekali — behavior identik seperti sebelumnya (satu koneksi buat semua), tidak ada perubahan apa pun. Fitur ini murni opt-in.
  • sticky (default false, sama seperti Laravel) — kalau true, begitu ada write di request yang sedang berjalan, SEMUA baca berikutnya (bukan cuma ke tabel yang barusan ditulis) ikut lewat koneksi write sampai request selesai. Ini setting global per proses (keterbatasan Phalcon\Mvc\Model\Manager, bukan per-koneksi murni seperti Laravel), diambil dari koneksi default.
  • Query raw lewat Core\Facades\DB/db service langsung (bukan lewat Model) SELALU lewat koneksi write — split otomatis ini cuma berlaku buat Model ActiveRecord. Buat baca manual lewat replica, resolve service db.read (atau db.{key}.read) secara eksplisit.
  • Model yang mau opt-out dari auto-split (misal tabel yang butuh baca selalu konsisten dari primary) daftarkan koneksi terpisah di database.connections TANPA key 'read' (mis. 'primary_only'), lalu di model panggil $this->setConnectionService('db.primary_only'); di initialize() — karena db.primary_only.read tidak pernah didaftarkan, listener bawaan otomatis skip model itu. Model yang di initialize()-nya sudah panggil setReadConnectionService() custom sendiri (ke connection service manapun yang beda dari write-nya) juga tidak akan ditimpa.