Skip to content

Commit 3070890

Browse files
committed
Add DbConnection adapters
1 parent 553506c commit 3070890

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cog\DbLocker\DbConnection;
6+
7+
interface DbConnectionInterface
8+
{
9+
/**
10+
* Execute a SQL query and return the first column of the first row.
11+
*
12+
* @param string $sql The SQL query to execute
13+
* @param array<string, mixed> $parameters Parameters to bind to the query
14+
* @return mixed The value from the first column of the first row
15+
*/
16+
public function executeAndFetchColumn(string $sql, array $parameters = []): mixed;
17+
18+
/**
19+
* Check if the connection is currently inside a transaction.
20+
*
21+
* @return bool True if inside a transaction, false otherwise
22+
*/
23+
public function isInTransaction(): bool;
24+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cog\DbLocker\DbConnection;
6+
7+
use Doctrine\DBAL\Connection;
8+
use Doctrine\DBAL\Exception;
9+
use RuntimeException;
10+
11+
/**
12+
* Doctrine DBAL adapter for unified database connection interface.
13+
*
14+
* This adapter wraps a Doctrine DBAL Connection instance and provides
15+
* a consistent interface for database operations across different database drivers.
16+
*/
17+
final class DoctrineDbConnection implements
18+
DbConnectionInterface
19+
{
20+
public function __construct(
21+
private Connection $connection,
22+
) {}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function executeAndFetchColumn(string $sql, array $parameters = []): mixed
28+
{
29+
try {
30+
return $this->connection->fetchOne($sql, $parameters);
31+
} catch (Exception $e) {
32+
throw new RuntimeException(
33+
sprintf(
34+
'Doctrine DBAL error while executing SQL query: %s. Error: %s',
35+
$sql,
36+
$e->getMessage(),
37+
),
38+
previous: $e,
39+
);
40+
}
41+
}
42+
43+
/**
44+
* @inheritDoc
45+
*/
46+
public function isInTransaction(): bool
47+
{
48+
return $this->connection->isTransactionActive();
49+
}
50+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cog\DbLocker\DbConnection;
6+
7+
use Illuminate\Database\Connection;
8+
use Illuminate\Database\QueryException;
9+
use RuntimeException;
10+
11+
/**
12+
* Eloquent Database adapter for unified database connection interface.
13+
*
14+
* This adapter wraps an Illuminate\Database\Connection instance and provides
15+
* a consistent interface for database operations across different database drivers.
16+
*/
17+
final class EloquentDbConnection implements
18+
DbConnectionInterface
19+
{
20+
public function __construct(
21+
private Connection $connection,
22+
) {}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function executeAndFetchColumn(string $sql, array $parameters = []): mixed
28+
{
29+
try {
30+
// Convert named parameters to positional parameters for Eloquent
31+
$bindings = array_values($parameters);
32+
33+
$result = $this->connection->selectOne($sql, $bindings);
34+
35+
if ($result === null) {
36+
return null;
37+
}
38+
39+
// Convert stdClass to array and get first value
40+
$resultArray = (array)$result;
41+
42+
return reset($resultArray) ?: null;
43+
} catch (QueryException $e) {
44+
throw new RuntimeException(
45+
sprintf(
46+
'Eloquent Database error while executing SQL query: %s. Error: %s',
47+
$sql,
48+
$e->getMessage(),
49+
),
50+
previous: $e,
51+
);
52+
}
53+
}
54+
55+
/**
56+
* @inheritDoc
57+
*/
58+
public function isInTransaction(): bool
59+
{
60+
return $this->connection->transactionLevel() > 0;
61+
}
62+
}

src/DbConnection/PdoDbConnection.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Cog\DbLocker\DbConnection;
6+
7+
use PDO;
8+
use PDOStatement;
9+
use RuntimeException;
10+
11+
/**
12+
* PDO adapter for unified database connection interface.
13+
*
14+
* This adapter wraps a PDO instance and provides a consistent interface
15+
* for database operations across different database drivers.
16+
*/
17+
final class PdoDbConnection implements
18+
DbConnectionInterface
19+
{
20+
public function __construct(
21+
private PDO $pdo,
22+
) {}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function executeAndFetchColumn(string $sql, array $parameters = []): mixed
28+
{
29+
$statement = $this->prepareAndExecute($sql, $parameters);
30+
31+
$result = $statement->fetchColumn(0);
32+
33+
// PDO fetchColumn returns false when no rows are found
34+
// We need to distinguish between false as a value and no result
35+
if ($result === false && $statement->rowCount() === 0) {
36+
return null;
37+
}
38+
39+
return $result;
40+
}
41+
42+
/**
43+
* @inheritDoc
44+
*/
45+
public function isInTransaction(): bool
46+
{
47+
return $this->pdo->inTransaction();
48+
}
49+
50+
/**
51+
* Prepare and execute a SQL statement with parameters.
52+
*
53+
* @param string $sql The SQL query to execute
54+
* @param array<string, mixed> $parameters Parameters to bind to the query
55+
* @return PDOStatement The executed statement
56+
* @throws RuntimeException If statement preparation or execution fails
57+
*/
58+
private function prepareAndExecute(string $sql, array $parameters): PDOStatement
59+
{
60+
try {
61+
$statement = $this->pdo->prepare($sql);
62+
63+
if ($statement === false) {
64+
$errorInfo = $this->pdo->errorInfo();
65+
throw new RuntimeException(
66+
sprintf(
67+
'Failed to prepare SQL statement: %s. Error: %s',
68+
$sql,
69+
$errorInfo[2] ?? 'Unknown error',
70+
),
71+
);
72+
}
73+
74+
$success = $statement->execute($parameters);
75+
76+
if (!$success) {
77+
$errorInfo = $statement->errorInfo();
78+
throw new RuntimeException(
79+
sprintf(
80+
'Failed to execute SQL statement: %s. Error: %s',
81+
$sql,
82+
$errorInfo[2] ?? 'Unknown error',
83+
),
84+
);
85+
}
86+
87+
return $statement;
88+
} catch (\PDOException $e) {
89+
throw new RuntimeException(
90+
sprintf(
91+
'PDO error while executing SQL statement: %s. Error: %s',
92+
$sql,
93+
$e->getMessage(),
94+
),
95+
previous: $e,
96+
);
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)