-
Notifications
You must be signed in to change notification settings - Fork 0
Static Facade
InitORM\Database\Facade\DB is a static, single-instance facade over a shared DatabaseInterface. Call any Database / QueryBuilder method on it; the call is forwarded to the underlying instance via __callStatic.
use InitORM\Database\Facade\DB;
DB::createImmutable($cfg);
// Anywhere afterwards:
$users = DB::read('users')->asAssoc()->rows();
DB::transaction(fn ($db) => $db->create('audit', [...]));The facade is fully opt-in. If you prefer dependency-injected DatabaseInterface, skip it entirely and instantiate Database directly — see Multiple Connections.
┌────────────────────────────┐
│ DB::createImmutable($cfg) │ ← required, exactly once
└──────────────┬─────────────┘
│
▼
┌────────────────────────────┐
│ DB::someMethod(...) │ ← any DatabaseInterface / builder method
└────────────────────────────┘
If you call DB::someMethod() before DB::createImmutable(), you get a clear error:
DatabaseException: "No immutable Database instance is configured.
Call DB::createImmutable($connection) first."
createImmutable() only succeeds once. A second call throws:
DB::createImmutable($cfg);
DB::createImmutable($cfg); // DatabaseException: "An immutable Database instance has already been set."This is intentional — silent overrides are a bug magnet. If you genuinely need to swap (typically in tests), use the explicit replaceImmutable():
DB::replaceImmutable($newCfg); // credentials array
DB::replaceImmutable($connection); // ConnectionInterface
DB::replaceImmutable($otherDatabase); // DatabaseInterface
DB::replaceImmutable(null); // clear the slotThe name says what it does.
When you want a second database without touching the facade slot, use DB::connect():
DB::createImmutable($primaryCfg);
$replica = DB::connect($replicaCfg); // returns DatabaseInterface, doesn't change DB::getDatabase()
$primary = DB::read('users'); // hits primary
$rows = $replica->read('users')->asAssoc()->rows(); // hits replicaDB::connect() is a thin alias of new Database($cfg). Pick whichever reads better in your codebase.
When you need the underlying DatabaseInterface (e.g. to pass it into another component), call DB::getDatabase():
$db = DB::getDatabase();
$repository = new UserRepository($db);DB::__callStatic($name, $arguments) forwards to getDatabase()->$name(...$arguments). This means every method on DatabaseInterface + every method on QueryBuilderInterface is reachable on the facade. The DocBlock on DB enumerates them all so IDE autocomplete works out of the box:
DB::select('id', 'name') // QueryBuilder method
->where('active', '=', 1) // QueryBuilder method
->orderBy('id', 'DESC') // QueryBuilder method
->limit(10) // QueryBuilder method
->read('users') // Database method
->asAssoc() // DataMapper method
->rows(); // DataMapper methodThe class is final and the constructor is private:
new DB(); // Error — Call to private InitORM\Database\Facade\DB::__construct()This is intentional — the facade is pure static state. Anything else would mislead readers.
Two patterns work well.
final class UserServiceTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
DB::replaceImmutable(new Database([
'driver' => 'sqlite', 'database' => ':memory:', 'charset' => '',
]));
DB::query('CREATE TABLE users (...)');
}
protected function tearDown(): void
{
DB::replaceImmutable(null); // clear for the next test
parent::tearDown();
}
}final class UserServiceTest extends TestCase
{
private Database $db;
protected function setUp(): void
{
$this->db = new Database([
'driver' => 'sqlite', 'database' => ':memory:', 'charset' => '',
]);
}
}This is what the package's own tests do — see tests/AbstractDatabaseTestCase.php.
-
Libraries — never bind your library users to a particular facade; accept
DatabaseInterfaceinstead. - Multi-tenant apps where the "current" database depends on request context — thread the instance explicitly.
- Applications with a DI container — your container is already a better place for the singleton.
For everything else (CLI scripts, single-DB web apps, prototypes), the facade is great.
- Multiple Connections — facade + secondary instances, sibling builders
-
Architecture — what
__callStaticactually does -
FAQ — "why is
createImmutablenamed that way?", "how do I clear the facade in PHPUnit?"
InitORM Database · MIT · maintained by Muhammet ŞAFAK · part of the InitORM stack
Getting Started
Core Operations
Cross-Cutting
Reference
Upgrading
Project