2024-06-08 23:58:45 +00:00
|
|
|
<?php declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace BitBadger\PDODocument;
|
|
|
|
|
|
|
|
use BitBadger\PDODocument\Mapper\Mapper;
|
|
|
|
use PDO;
|
|
|
|
use PDOException;
|
|
|
|
use PDOStatement;
|
2024-06-25 02:04:11 +00:00
|
|
|
use PhpOption\{None, Option, Some};
|
2024-06-08 23:58:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
2024-06-25 02:04:11 +00:00
|
|
|
* @return Option<TDoc> A `Some` instance if the item is found, `None` otherwise
|
2024-06-08 23:58:45 +00:00
|
|
|
* @throws DocumentException If any is encountered
|
|
|
|
*/
|
2024-06-25 02:04:11 +00:00
|
|
|
public static function single(string $query, array $parameters, Mapper $mapper): Option
|
2024-06-08 23:58:45 +00:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
$stmt = &self::runQuery("$query LIMIT 1", $parameters);
|
2024-06-25 02:04:11 +00:00
|
|
|
return ($first = $stmt->fetch(PDO::FETCH_ASSOC)) ? Some::create($mapper->map($first)) : None::create();
|
2024-06-08 23:58:45 +00:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|