Doctrine DBAL

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

# Doctrine DBAL

DBM Framework's database package does not depend on Doctrine. It defines DatabaseInterface, SELECT and CRUD builder contracts, result handling, and hydration. DBM Platform supplies DoctrineDatabaseAdapter and a SELECT-builder adapter so an application can choose Doctrine DBAL while repositories continue to depend on the framework contract.

# Selecting the platform adapter

DBM Platform reads DB_DRIVER as <adapter>|<driver>:

DB_DRIVER=DOCTRINE|pdo_mysql

The platform's DatabaseFactory creates a Doctrine DBAL connection and wraps it in DoctrineDatabaseAdapter. PDO options include PDO|pdo_mysql, PDO|pdo_pgsql, and PDO|pdo_sqlite; PDO requires the matching extension. The factory defaults to PDO|pdo_mysql when the variable is absent.

The platform project currently includes doctrine/dbal as a Composer dependency. The factory also handles deployments where Doctrine is not loaded: in development it reports the missing package; in other environments it logs the issue and falls back to PDO using the selected driver. This fallback is platform behavior, not a framework guarantee.

# Shared API and adapter boundary

Both platform adapters implement DatabaseInterface for query execution, transactions, result fetching, hydration, last insert ID, and connection closing. createQueryBuilder() returns an adapter for the common SELECT builder API. With Doctrine selected, that adapter delegates to Doctrine's query builder; PDO uses the framework's PdoSelectQueryBuilder.

builder() returns the framework CRUD builder for both adapters. It creates SQL and a QueryResult; the selected adapter executes it. Thus common repository code can share DatabaseInterface, but the underlying SQL and database capabilities are not made identical.

$qb = $database->createQueryBuilder();
$qb->select('u.id', 'u.email')
    ->from('dbm_user', 'u')
    ->where('u.status = :status')
    ->setParameter('status', 'active');

$users = $database->fetchAll($qb->getSQL(), $qb->getParameters());

# Choosing between adapters

Choose PDO when the built-in adapter covers the application's needs and you want a small direct PDO integration. Choose Doctrine DBAL when the platform project needs DBAL's connection and query-builder integration. Both paths use the framework contract, so inject DatabaseInterface into repositories rather than depending on a concrete adapter.

For complex reporting, correlated subqueries, vendor-specific functions, upserts, or custom SQL, direct SQL is supported through fetch(), fetchAll(), or execute(). Bind all values. SQL identifiers and vendor-specific syntax remain tied to the target database; the DBM adapter does not translate arbitrary SQL between drivers. See Database Queries for builder, CRUD, raw SQL, and transaction examples.