Connection

A thin PDO wrapper with convenience methods, transactions, and savepoint support.

Table of contents

Creating a Connection

From DSN parameters

use AsceticSoft\Rowcast\Connection;

$connection = Connection::create(
    dsn: 'mysql:host=localhost;dbname=app',
    username: 'root',
    password: 'secret',
);

From an existing PDO instance

$pdo = new \PDO('sqlite::memory:');
$connection = new Connection($pdo);

Connection automatically sets PDO::ATTR_ERRMODE to PDO::ERRMODE_EXCEPTION to ensure all database errors throw exceptions.


Running Raw Queries

executeQuery — SELECT queries

Returns a PDOStatement for reading results:

$stmt = $connection->executeQuery('SELECT * FROM users WHERE id = ?', [1]);

executeStatement — INSERT/UPDATE/DELETE

Returns the number of affected rows:

$affected = $connection->executeStatement(
    'UPDATE users SET name = ? WHERE id = ?',
    ['Alice', 1],
);

Fetch helpers

// Fetch all rows as associative arrays
$rows = $connection->fetchAllAssociative('SELECT * FROM users');

// Fetch a single row
$row = $connection->fetchAssociative('SELECT * FROM users WHERE id = ?', [1]);

// Fetch a single scalar value
$count = $connection->fetchOne('SELECT COUNT(*) FROM users');

Transactions

Manual transaction management

$connection->beginTransaction();
try {
    $connection->executeStatement('INSERT INTO users (name) VALUES (?)', ['Alice']);
    $connection->executeStatement('INSERT INTO users (name) VALUES (?)', ['Bob']);
    $connection->commit();
} catch (\Throwable $e) {
    $connection->rollBack();
    throw $e;
}
$connection->transactional(function (Connection $conn) {
    $conn->executeStatement('INSERT INTO users (name) VALUES (?)', ['Alice']);
    $conn->executeStatement('INSERT INTO users (name) VALUES (?)', ['Bob']);
});

The transactional() method automatically commits on success and rolls back on exception.


Nested Transactions (Savepoints)

By default, calling beginTransaction() inside an active transaction will fail. Enable savepoint-based nesting:

// Via factory
$connection = Connection::create(
    'mysql:host=localhost;dbname=app', 'root', 'secret',
    nestTransactions: true,
);

// Via constructor
$connection = new Connection($pdo, nestTransactions: true);

When enabled, inner beginTransaction() calls create SQL SAVEPOINTs, and commit() / rollBack() release or roll back to the corresponding savepoint:

$connection->transactional(function (Connection $conn) {
    $conn->executeStatement('INSERT INTO users (name) VALUES (?)', ['Alice']);

    try {
        $conn->transactional(function (Connection $inner) {
            $inner->executeStatement('INSERT INTO users (name) VALUES (?)', ['Bob']);
            throw new \RuntimeException('inner failure');
        });
    } catch (\RuntimeException) {
        // Only the inner transaction (Bob) is rolled back.
        // Alice's insert is preserved.
    }
});
// Alice is committed; Bob is not.

Checking nesting level

$level = $connection->getTransactionNestingLevel();
// 0 — no active transaction

Query Builder

Create a fluent query builder from the connection:

$qb = $connection->createQueryBuilder();

$rows = $qb->select('id', 'name')
    ->from('users')
    ->where('status = :status')
    ->setParameter('status', 'active')
    ->fetchAllAssociative();

See the Query Builder page for the full reference.


Accessing the underlying PDO

$pdo = $connection->getPdo();

Use direct PDO access sparingly. Prefer using Connection methods to maintain consistent error handling and transaction management.