Use PDO singleton
This commit is contained in:
parent
bcca9f5ace
commit
d9ffc36fe6
@ -27,6 +27,9 @@ class Configuration
|
||||
/** @var Mode|null The mode in which the library is operating (filled after first connection if not configured) */
|
||||
public static ?Mode $mode = null;
|
||||
|
||||
/** @var PDO|null The PDO instance to use for database commands */
|
||||
private static ?PDO $_pdo = null;
|
||||
|
||||
/**
|
||||
* Retrieve a new connection to the database
|
||||
*
|
||||
@ -35,21 +38,24 @@ class Configuration
|
||||
*/
|
||||
public static function dbConn(): PDO
|
||||
{
|
||||
if (empty(self::$pdoDSN)) {
|
||||
throw new DocumentException('Please provide a data source name (DSN) before attempting data access');
|
||||
}
|
||||
$db = new PDO(self::$pdoDSN, $_ENV['PDO_DOC_USERNAME'] ?? self::$username,
|
||||
$_ENV['PDO_DOC_PASSWORD'] ?? self::$password, self::$options);
|
||||
if (is_null(self::$_pdo)) {
|
||||
if (empty(self::$pdoDSN)) {
|
||||
throw new DocumentException('Please provide a data source name (DSN) before attempting data access');
|
||||
}
|
||||
self::$_pdo = new PDO(self::$pdoDSN, $_ENV['PDO_DOC_USERNAME'] ?? self::$username,
|
||||
$_ENV['PDO_DOC_PASSWORD'] ?? self::$password, self::$options);
|
||||
|
||||
if (is_null(self::$mode)) {
|
||||
$driver = $db->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
self::$mode = match ($driver) {
|
||||
'pgsql' => Mode::PgSQL,
|
||||
'sqlite' => Mode::SQLite,
|
||||
default => throw new DocumentException(
|
||||
"Unsupported driver $driver: this library currently supports PostgreSQL and SQLite")
|
||||
};
|
||||
if (is_null(self::$mode)) {
|
||||
$driver = self::$_pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
self::$mode = match ($driver) {
|
||||
'pgsql' => Mode::PgSQL,
|
||||
'sqlite' => Mode::SQLite,
|
||||
default => throw new DocumentException(
|
||||
"Unsupported driver $driver: this library currently supports PostgreSQL and SQLite")
|
||||
};
|
||||
}
|
||||
}
|
||||
return $db;
|
||||
|
||||
return self::$_pdo;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use BitBadger\PDODocument\Mapper\CountMapper;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to count documents
|
||||
@ -14,13 +13,12 @@ class Count
|
||||
* Count all documents in a table
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @return int The count of documents in the table
|
||||
* @throws DocumentException If one is encountered
|
||||
*/
|
||||
public static function all(string $tableName, ?PDO $pdo = null): int
|
||||
public static function all(string $tableName): int
|
||||
{
|
||||
return Custom::scalar(Query\Count::all($tableName), [], new CountMapper(), $pdo);
|
||||
return Custom::scalar(Query\Count::all($tableName), [], new CountMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,16 +26,14 @@ class Count
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @param array|Field[] $fields The field comparison to match
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`)
|
||||
* @return int The count of documents matching the field comparison
|
||||
* @throws DocumentException If one is encountered
|
||||
*/
|
||||
public static function byFields(string $tableName, array $fields, ?PDO $pdo = null,
|
||||
string $conjunction = 'AND'): int
|
||||
public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): int
|
||||
{
|
||||
$namedFields = Parameters::nameFields($fields);
|
||||
return Custom::scalar(Query\Count::byFields($tableName, $namedFields, $conjunction),
|
||||
Parameters::addFields($namedFields, []), new CountMapper(), $pdo);
|
||||
Parameters::addFields($namedFields, []), new CountMapper());
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,13 @@ class Custom
|
||||
*
|
||||
* @param string $query The query to be run
|
||||
* @param array $parameters The parameters for the query
|
||||
* @param PDO $pdo The database connection on which the query should be run
|
||||
* @return PDOStatement The result of executing the query
|
||||
* @throws DocumentException If the query execution is unsuccessful
|
||||
*/
|
||||
public static function runQuery(string $query, array $parameters, PDO $pdo): PDOStatement
|
||||
public static function &runQuery(string $query, array $parameters): PDOStatement
|
||||
{
|
||||
$debug = defined('PDO_DOC_DEBUG_SQL');
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt = Configuration::dbConn()->prepare($query);
|
||||
foreach ($parameters as $key => $value) {
|
||||
if ($debug) echo "<pre>Binding $value to $key\n</pre>";
|
||||
$dataType = match (true) {
|
||||
@ -77,19 +76,16 @@ class Custom
|
||||
* @param string $query The query to be executed (will have "LIMIT 1" appended)
|
||||
* @param array $parameters Parameters to use in executing the query
|
||||
* @param Mapper<TDoc> $mapper Mapper to deserialize the result
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @return false|TDoc The item if it is found, false if not
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function single(string $query, array $parameters, Mapper $mapper, ?PDO $pdo = null): mixed
|
||||
public static function single(string $query, array $parameters, Mapper $mapper): mixed
|
||||
{
|
||||
try {
|
||||
$stmt = self::runQuery("$query LIMIT 1", $parameters,
|
||||
is_null($pdo) ? $actualPDO = Configuration::dbConn() : $pdo);
|
||||
$stmt = &self::runQuery("$query LIMIT 1", $parameters);
|
||||
return ($first = $stmt->fetch(PDO::FETCH_ASSOC)) ? $mapper->map($first) : false;
|
||||
} finally {
|
||||
$stmt = null;
|
||||
if (isset($actualPDO)) $actualPDO = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,16 +94,14 @@ class Custom
|
||||
*
|
||||
* @param string $query The query to execute
|
||||
* @param array $parameters Parameters to use in executing the query
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function nonQuery(string $query, array $parameters, ?PDO $pdo = null): void
|
||||
public static function nonQuery(string $query, array $parameters): void
|
||||
{
|
||||
try {
|
||||
$stmt = self::runQuery($query, $parameters, is_null($pdo) ? $actualPDO = Configuration::dbConn() : $pdo);
|
||||
$stmt = &self::runQuery($query, $parameters);
|
||||
} finally {
|
||||
$stmt = null;
|
||||
if (isset($actualPDO)) $actualPDO = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,18 +112,16 @@ class Custom
|
||||
* @param string $query The query to retrieve the value
|
||||
* @param array $parameters Parameters to use in executing the query
|
||||
* @param Mapper<T> $mapper The mapper to obtain the result
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @return mixed|false|T The scalar value if found, false if not
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function scalar(string $query, array $parameters, Mapper $mapper, ?PDO $pdo = null): mixed
|
||||
{
|
||||
try {
|
||||
$stmt = self::runQuery($query, $parameters, is_null($pdo) ? $actualPDO = Configuration::dbConn() : $pdo);
|
||||
$stmt = &self::runQuery($query, $parameters);
|
||||
return ($first = $stmt->fetch(PDO::FETCH_NUM)) ? $mapper->map($first) : false;
|
||||
} finally {
|
||||
$stmt = null;
|
||||
if (isset($actualPDO)) $actualPDO = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to create tables and indexes
|
||||
*/
|
||||
@ -13,13 +11,12 @@ class Definition
|
||||
* Ensure a document table exists
|
||||
*
|
||||
* @param string $name The name of the table to be created if it does not exist
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function ensureTable(string $name, ?PDO $pdo = null): void
|
||||
public static function ensureTable(string $name): void
|
||||
{
|
||||
Custom::nonQuery(Query\Definition::ensureTable($name), [], $pdo);
|
||||
Custom::nonQuery(Query\Definition::ensureKey($name), [], $pdo);
|
||||
Custom::nonQuery(Query\Definition::ensureTable($name), []);
|
||||
Custom::nonQuery(Query\Definition::ensureKey($name), []);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -28,11 +25,10 @@ class Definition
|
||||
* @param string $tableName The name of the table which should be indexed
|
||||
* @param string $indexName The name of the index
|
||||
* @param array $fields Fields which should be a part of this index
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function ensureFieldIndex(string $tableName, string $indexName, array $fields, ?PDO $pdo = null): void
|
||||
public static function ensureFieldIndex(string $tableName, string $indexName, array $fields): void
|
||||
{
|
||||
Custom::nonQuery(Query\Definition::ensureIndexOn($tableName, $indexName, $fields), [], $pdo);
|
||||
Custom::nonQuery(Query\Definition::ensureIndexOn($tableName, $indexName, $fields), []);
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to delete documents
|
||||
*/
|
||||
@ -14,12 +12,11 @@ class Delete
|
||||
*
|
||||
* @param string $tableName The table from which the document should be deleted
|
||||
* @param mixed $docId The ID of the document to be deleted
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, ?PDO $pdo = null): void
|
||||
public static function byId(string $tableName, mixed $docId): void
|
||||
{
|
||||
Custom::nonQuery(Query\Delete::byId($tableName), Parameters::id($docId), $pdo);
|
||||
Custom::nonQuery(Query\Delete::byId($tableName), Parameters::id($docId));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,15 +24,13 @@ class Delete
|
||||
*
|
||||
* @param string $tableName The table from which documents should be deleted
|
||||
* @param array|Field[] $fields The field comparison to match
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byFields(string $tableName, array $fields, ?PDO $pdo = null,
|
||||
string $conjunction = 'AND'): void
|
||||
public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): void
|
||||
{
|
||||
$namedFields = Parameters::nameFields($fields);
|
||||
Custom::nonQuery(Query\Delete::byFields($tableName, $namedFields, $conjunction),
|
||||
Parameters::addFields($namedFields, []), $pdo);
|
||||
Parameters::addFields($namedFields, []));
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions that apply at a whole document level
|
||||
*/
|
||||
@ -14,12 +12,11 @@ class Document
|
||||
*
|
||||
* @param string $tableName The name of the table into which the document should be inserted
|
||||
* @param array|object $document The document to be inserted
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function insert(string $tableName, array|object $document, ?PDO $pdo = null): void
|
||||
public static function insert(string $tableName, array|object $document): void
|
||||
{
|
||||
Custom::nonQuery(Query::insert($tableName), Parameters::json(':data', $document), $pdo);
|
||||
Custom::nonQuery(Query::insert($tableName), Parameters::json(':data', $document));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,12 +24,11 @@ class Document
|
||||
*
|
||||
* @param string $tableName The name of the table to which the document should be saved
|
||||
* @param array|object $document The document to be saved
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function save(string $tableName, array|object $document, ?PDO $pdo = null): void
|
||||
public static function save(string $tableName, array|object $document): void
|
||||
{
|
||||
Custom::nonQuery(Query::save($tableName), Parameters::json(':data', $document), $pdo);
|
||||
Custom::nonQuery(Query::save($tableName), Parameters::json(':data', $document));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,12 +37,11 @@ class Document
|
||||
* @param string $tableName The table in which the document should be updated
|
||||
* @param mixed $docId The ID of the document to be updated
|
||||
* @param array|object $document The document to be updated
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function update(string $tableName, mixed $docId, array|object $document, ?PDO $pdo = null): void
|
||||
public static function update(string $tableName, mixed $docId, array|object $document): void
|
||||
{
|
||||
Custom::nonQuery(Query::update($tableName),
|
||||
array_merge(Parameters::id($docId), Parameters::json(':data', $document)), $pdo);
|
||||
array_merge(Parameters::id($docId), Parameters::json(':data', $document)));
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,10 @@ class DocumentList
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param PDO|null $pdo The database connection against which the query was opened
|
||||
* @param PDOStatement|null $result The result of the query
|
||||
* @param Mapper<TDoc> $mapper The mapper to deserialize JSON
|
||||
*/
|
||||
private function __construct(private ?PDO $pdo, private ?PDOStatement $result, private Mapper $mapper) { }
|
||||
private function __construct(private ?PDOStatement &$result, private readonly Mapper $mapper) { }
|
||||
|
||||
/**
|
||||
* Construct a new document list
|
||||
@ -35,8 +34,8 @@ class DocumentList
|
||||
*/
|
||||
public static function create(string $query, array $parameters, Mapper $mapper): static
|
||||
{
|
||||
$pdo = Configuration::dbConn();
|
||||
return new static($pdo, Custom::runQuery($query, $parameters, $pdo), $mapper);
|
||||
$stmt = &Custom::runQuery($query, $parameters);
|
||||
return new static($stmt, $mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,6 +49,5 @@ class DocumentList
|
||||
while ($row = $this->result->fetch(PDO::FETCH_ASSOC)) yield $this->mapper->map($row);
|
||||
}
|
||||
$this->result = null;
|
||||
$this->pdo = null;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use BitBadger\PDODocument\Mapper\ExistsMapper;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to determine if documents exist
|
||||
@ -15,13 +14,12 @@ class Exists
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be determined
|
||||
* @param mixed $docId The ID of the document whose existence should be determined
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @return bool True if the document exists, false if not
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, ?PDO $pdo = null): bool
|
||||
public static function byId(string $tableName, mixed $docId): bool
|
||||
{
|
||||
return Custom::scalar(Query\Exists::byId($tableName), Parameters::id($docId), new ExistsMapper(), $pdo);
|
||||
return Custom::scalar(Query\Exists::byId($tableName), Parameters::id($docId), new ExistsMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -29,16 +27,14 @@ class Exists
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be determined
|
||||
* @param Field[] $fields The field comparison to match
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`)
|
||||
* @return bool True if any documents match the field comparison, false if not
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byFields(string $tableName, array $fields, ?PDO $pdo = null,
|
||||
string $conjunction = 'AND'): bool
|
||||
public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): bool
|
||||
{
|
||||
$namedFields = Parameters::nameFields($fields);
|
||||
return Custom::scalar(Query\Exists::byFields($tableName, $namedFields, $conjunction),
|
||||
Parameters::addFields($namedFields, []), new ExistsMapper(), $pdo);
|
||||
Parameters::addFields($namedFields, []), new ExistsMapper());
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to patch (partially update) documents
|
||||
*/
|
||||
@ -15,13 +13,12 @@ class Patch
|
||||
* @param string $tableName The table in which the document should be patched
|
||||
* @param mixed $docId The ID of the document to be patched
|
||||
* @param array|object $patch The object with which the document should be patched (will be JSON-encoded)
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered (database mode must be set)
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, array|object $patch, ?PDO $pdo = null): void
|
||||
public static function byId(string $tableName, mixed $docId, array|object $patch): void
|
||||
{
|
||||
Custom::nonQuery(Query\Patch::byId($tableName),
|
||||
array_merge(Parameters::id($docId), Parameters::json(':data', $patch)), $pdo);
|
||||
array_merge(Parameters::id($docId), Parameters::json(':data', $patch)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,15 +27,14 @@ class Patch
|
||||
* @param string $tableName The table in which documents should be patched
|
||||
* @param array|Field[] $fields The field comparison to match
|
||||
* @param array|object $patch The object with which the documents should be patched (will be JSON-encoded)
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byFields(string $tableName, array $fields, array|object $patch, ?PDO $pdo = null,
|
||||
public static function byFields(string $tableName, array $fields, array|object $patch,
|
||||
string $conjunction = 'AND'): void
|
||||
{
|
||||
$namedFields = Parameters::nameFields($fields);
|
||||
Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $conjunction),
|
||||
Parameters::addFields($namedFields, Parameters::json(':data', $patch)), $pdo);
|
||||
Parameters::addFields($namedFields, Parameters::json(':data', $patch)));
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Functions to remove fields from documents
|
||||
*/
|
||||
@ -15,14 +13,13 @@ class RemoveFields
|
||||
* @param string $tableName The table in which the document should have fields removed
|
||||
* @param mixed $docId The ID of the document from which fields should be removed
|
||||
* @param array|string[] $fieldNames The names of the fields to be removed
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, array $fieldNames, ?PDO $pdo = null): void
|
||||
public static function byId(string $tableName, mixed $docId, array $fieldNames): void
|
||||
{
|
||||
$nameParams = Parameters::fieldNames(':name', $fieldNames);
|
||||
Custom::nonQuery(Query\RemoveFields::byId($tableName, $nameParams),
|
||||
array_merge(Parameters::id($docId), $nameParams), $pdo);
|
||||
array_merge(Parameters::id($docId), $nameParams));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,16 +28,15 @@ class RemoveFields
|
||||
* @param string $tableName The table in which documents should have fields removed
|
||||
* @param array|Field[] $fields The field comparison to match
|
||||
* @param array|string[] $fieldNames The names of the fields to be removed
|
||||
* @param PDO|null $pdo The database connection to use (optional; will obtain one if not provided)
|
||||
* @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`)
|
||||
* @throws DocumentException If any is encountered
|
||||
*/
|
||||
public static function byFields(string $tableName, array $fields, array $fieldNames, ?PDO $pdo = null,
|
||||
public static function byFields(string $tableName, array $fields, array $fieldNames,
|
||||
string $conjunction = 'AND'): void
|
||||
{
|
||||
$nameParams = Parameters::fieldNames(':name', $fieldNames);
|
||||
$namedFields = Parameters::nameFields($fields);
|
||||
Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $conjunction),
|
||||
Parameters::addFields($namedFields, $nameParams), $pdo);
|
||||
Parameters::addFields($namedFields, $nameParams));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user