Database Layer
The documentation is constantly evolving and updated alongside the project's progress. Since it is developed in parallel with the code, some sections may temporarily refer to earlier versions of the system-these are gradually adapted to the current state of the application. If you notice any outdated information, please report it or open an Issue / Pull Request.
Documentation
# Database Layer
DBM Framework defines a database contract and provides a PDO adapter. Applications create and register the adapter they want to use; the framework does not choose a connection or load credentials on its own. DBM Platform adds a DatabaseFactory that selects PDO or Doctrine DBAL from DB_DRIVER.
The central contract is Dbm\Database\Contracts\DatabaseInterface. It provides SQL execution (query, fetch, fetchAll, execute), two different builders, transactions, result hydration, last-insert-ID access, and connection utilities. It also defines databaseExists(), selectDatabase(), and importSqlFile(); whether those operations are supported depends on the adapter and database platform. query() returns a ResultInterface with fetch(), fetchAll(), and affectedRows(). PdoDatabaseAdapter is the framework PDO implementation. The platform's DoctrineDatabaseAdapter implements the same contract around a Doctrine DBAL connection.
# Query APIs
The framework deliberately separates query composition:
createQueryBuilder()returns a fluent SELECT builder. Its common API supports selected columns, table aliases, joins, conditions,INfilters, ordering, limits, offsets, parameters, and expressions.builder()returns a CRUD builder for constructingINSERT,UPDATE, andDELETEstatements. It returns aQueryResultwithsqlandparams; execute it throughDatabaseInterface::execute().- For complex, dialect-specific, or unusual statements, write SQL directly and pass values as parameters. Builders are intended to make common queries and CRUD operations convenient, not to hide SQL or replace it in every case.
These builders are SQL construction tools, not a full ORM: they do not track entities or synchronize object changes automatically. See Database Queries for practical examples and Repositories and Hydration for the application pattern.
# Registering a connection
The framework application must bind DatabaseInterface to a concrete adapter or factory. DBM Platform's DatabaseFactory can be registered as a lazy singleton, for example in bin/services.php for CLI services:
use App\Infrastructure\Database\DatabaseFactory;
use Dbm\Core\DependencyContainer;
use Dbm\Database\Contracts\DatabaseInterface;
return static function (DependencyContainer $container): void {
$container->singleton(
DatabaseInterface::class,
static fn(): DatabaseInterface => (new DatabaseFactory())->create()
);
};
The factory reads environment values when it creates the connection. Keep credentials in the application environment and do not hard-code them in repositories. The framework's DatabaseEnvValidator checks for DB_HOST and DB_USER; operations that require a named database also require DB_NAME.
# Driver selection in DBM Platform
The platform recognizes these DB_DRIVER values:
DB_DRIVER=PDO|pdo_mysql
# PDO|pdo_pgsql
# PDO|pdo_sqlite
# DOCTRINE|pdo_mysql
PDO supports MySQL, PostgreSQL, and SQLite when the corresponding PDO driver is installed. In the current framework adapter, pdo_sqlite uses sqlite::memory:; the database exists only for the lifetime of that connection. Doctrine selection uses a Doctrine DBAL driver name and is described in Doctrine DBAL.
These DB_DRIVER values and fallback rules belong to DBM Platform. A separate framework application can choose another factory or adapter, provided it implements DatabaseInterface.
# Safety and portability
Bind untrusted values with named parameters. Table names, column names, and sort directions are SQL structure, not bindable values; choose them from trusted code or an allowlist. The PDO expr()->in() helper builds SQL text from its arguments; prefer whereIn() for variable value lists because it creates bound placeholders. Treat SqlExpression::raw() as trusted SQL only.
The shared builder API makes common query composition familiar across adapters, but raw SQL, expressions, driver behavior, and database features remain dialect-specific. Prefer a repository for persistence logic and see Doctrine DBAL before switching the platform adapter.