databases storage and caching

Doctrine DBAL Orientation

Doctrine DBAL is a database abstraction layer. It sits above PDO and gives PHP applications a consistent API for connections, queries, parameter types, transactions, schema inspection, and database-platform differences.

DBAL is not the same as Doctrine ORM. DBAL works with SQL and result rows. ORM maps database rows to objects and tracks object changes.

When DBAL Is Useful

DBAL is useful when a project wants more structure than raw PDO but does not want a full ORM for every query.

Common uses include:

  • Symfony applications.
  • Doctrine Migrations.
  • Query builders for dynamic SQL.
  • Database platform abstraction.
  • Schema inspection and schema comparison.
  • Typed parameters for arrays, booleans, JSON, and dates.

Connection Configuration

DBAL connections are usually created by framework configuration. Conceptually, the configuration contains driver, host, database, credentials, and charset.

PHP example
<?php

declare(strict_types=1);

$config = [
    'driver' => 'pdo_mysql',
    'host' => '127.0.0.1',
    'dbname' => 'app',
    'user' => 'app_user',
    'charset' => 'utf8mb4',
];

print_r($config);

// Prints:
// [driver] => pdo_mysql
// [charset] => utf8mb4

Do not hard-code production passwords in PHP arrays. Use the framework's environment-aware configuration.

Queries And Parameters

DBAL lets code execute SQL with parameters.

PHP example
<?php

declare(strict_types=1);

$sql = 'SELECT id, email FROM users WHERE status = :status ORDER BY id';
$parameters = ['status' => 'active'];

echo $sql . PHP_EOL;
echo json_encode($parameters, JSON_THROW_ON_ERROR) . PHP_EOL;

// Prints:
// SELECT id, email FROM users WHERE status = :status ORDER BY id
// {"status":"active"}

In real DBAL code, methods such as executeQuery() and fetchAssociative() are commonly used. The important habit remains the same: bind values instead of concatenating them.

Query Builder

DBAL includes a query builder. It can help assemble dynamic SQL while keeping parameters separate.

PHP example
<?php

declare(strict_types=1);

function userSearchParts(?string $status): array
{
    $parts = [
        'select' => 'u.id, u.email',
        'from' => 'users u',
        'where' => [],
        'parameters' => [],
    ];

    if ($status !== null) {
        $parts['where'][] = 'u.status = :status';
        $parts['parameters']['status'] = $status;
    }

    return $parts;
}

print_r(userSearchParts('active'));

// Prints:
// [where] => u.status = :status

Query builders do not make unsafe logic safe automatically. You still need allow-lists for dynamic column names and sort directions.

Transactions

DBAL provides transaction helpers and explicit transaction methods. Use transactions for multi-step database changes just as you would with PDO.

PHP example
<?php

declare(strict_types=1);

function dbalTransactionSteps(): array
{
    return ['beginTransaction', 'execute updates', 'commit or rollBack'];
}

print_r(dbalTransactionSteps());

// Prints:
// [0] => beginTransaction
// [1] => execute updates
// [2] => commit or rollBack

What To Check

Before moving on, make sure you can:

  • Explain the difference between DBAL and ORM.
  • Recognise DBAL connection configuration.
  • Use SQL with bound parameters.
  • Understand where query builders help and where they do not.
  • Use DBAL transactions for related writes.
  • Know that DBAL still requires SQL knowledge.

Practice

Practice: Recognise DBAL Responsibilities

Write a short PHP example that models DBAL-style query configuration.

Requirements

  • Show connection configuration for a DBAL connection.
  • Build a SQL string with a named parameter.
  • Keep parameters separate from SQL.
  • Include a query-builder style structure for optional filters.
  • Explain how DBAL differs from an ORM.
Show solution

This example models the shapes you should recognise when reading DBAL-backed code.

PHP example
<?php

declare(strict_types=1);

$connectionConfig = [
    'driver' => 'pdo_pgsql',
    'host' => '127.0.0.1',
    'dbname' => 'app',
    'user' => 'app_user',
];

function activeUsersQuery(?string $emailSearch = null): array
{
    $where = ['status = :status'];
    $parameters = ['status' => 'active'];

    if ($emailSearch !== null) {
        $where[] = 'email LIKE :email';
        $parameters['email'] = '%' . $emailSearch . '%';
    }

    return [
        'sql' => 'SELECT id, email FROM users WHERE ' . implode(' AND ', $where) . ' ORDER BY id',
        'parameters' => $parameters,
    ];
}

print_r($connectionConfig);
print_r(activeUsersQuery('example.com'));

// Prints:
// [driver] => pdo_pgsql
// [sql] => SELECT id, email FROM users WHERE status = :status AND email LIKE :email ORDER BY id

DBAL helps with connections, SQL execution, typed parameters, query building, transactions, and schema tooling. An ORM goes further by mapping rows to objects and tracking object persistence.