pdo-document/src/Custom.php

145 lines
5.4 KiB
PHP

<?php declare(strict_types=1);
namespace BitBadger\PDODocument;
use BitBadger\PDODocument\Mapper\Mapper;
use PDO;
use PDOException;
use PDOStatement;
use PhpOption\{None, Option, Some};
/**
* Functions to execute custom queries
*/
class Custom
{
/**
* Prepare a query for execution and run it
*
* @param string $query The query to be run
* @param array $parameters The parameters for the query
* @return PDOStatement The result of executing the query
* @throws DocumentException If the query execution is unsuccessful
*/
public static function &runQuery(string $query, array $parameters): PDOStatement
{
$debug = defined('PDO_DOC_DEBUG_SQL');
try {
$stmt = Configuration::dbConn()->prepare($query);
} catch (PDOException $ex) {
$keyword = explode(' ', $query, 2)[0];
throw new DocumentException(
sprintf("Error executing %s statement: [%s] %s", $keyword, Configuration::dbConn()->errorCode(),
Configuration::dbConn()->errorInfo()[2]),
previous: $ex);
}
foreach ($parameters as $key => $value) {
if ($debug) echo "<pre>Binding $value to $key\n</pre>";
$dataType = match (true) {
is_bool($value) => PDO::PARAM_BOOL,
is_int($value) => PDO::PARAM_INT,
is_null($value) => PDO::PARAM_NULL,
default => PDO::PARAM_STR
};
$stmt->bindValue($key, $value, $dataType);
}
if ($debug) echo '<pre>SQL: ' . $stmt->queryString . '</pre>';
try {
if ($stmt->execute()) return $stmt;
} catch (PDOException $ex) {
$keyword = explode(' ', $query, 2)[0];
throw new DocumentException(
sprintf("Error executing %s statement: [%s] %s", $keyword, $stmt->errorCode(), $stmt->errorInfo()[2]),
previous: $ex);
}
$keyword = explode(' ', $query, 2)[0];
throw new DocumentException("Error executing $keyword statement: " . $stmt->errorCode());
}
/**
* Execute a query that returns a list of results (lazy)
*
* @template TDoc The domain type of the document to retrieve
* @param string $query The query to be executed
* @param array $parameters Parameters to use in executing the query
* @param Mapper<TDoc> $mapper Mapper to deserialize the result
* @return DocumentList<TDoc> The items matching the query
* @throws DocumentException If any is encountered
*/
public static function list(string $query, array $parameters, Mapper $mapper): DocumentList
{
return DocumentList::create($query, $parameters, $mapper);
}
/**
* Execute a query that returns an array of results (eager)
*
* @template TDoc The domain type of the document to retrieve
* @param string $query The query to be executed
* @param array $parameters Parameters to use in executing the query
* @param Mapper<TDoc> $mapper Mapper to deserialize the result
* @return TDoc[] The items matching the query
* @throws DocumentException If any is encountered
*/
public static function array(string $query, array $parameters, Mapper $mapper): array
{
return iterator_to_array(self::list($query, $parameters, $mapper)->items());
}
/**
* Execute a query that returns one or no results (returns false if not found)
*
* @template TDoc The domain type of the document to retrieve
* @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
* @return Option<TDoc> A `Some` instance if the item is found, `None` otherwise
* @throws DocumentException If any is encountered
*/
public static function single(string $query, array $parameters, Mapper $mapper): Option
{
try {
$stmt = &self::runQuery("$query LIMIT 1", $parameters);
return ($first = $stmt->fetch(PDO::FETCH_ASSOC)) ? Some::create($mapper->map($first)) : None::create();
} finally {
$stmt = null;
}
}
/**
* Execute a query that does not return a value
*
* @param string $query The query to execute
* @param array $parameters Parameters to use in executing the query
* @throws DocumentException If any is encountered
*/
public static function nonQuery(string $query, array $parameters): void
{
try {
$stmt = &self::runQuery($query, $parameters);
} finally {
$stmt = null;
}
}
/**
* Execute a query that returns a scalar value
*
* @template T The scalar type to return
* @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
* @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): mixed
{
try {
$stmt = &self::runQuery($query, $parameters);
return ($first = $stmt->fetch(PDO::FETCH_NUM)) ? $mapper->map($first) : false;
} finally {
$stmt = null;
}
}
}