Database/src/Database.php

144 lines
4.1 KiB
PHP

<?php
namespace Nest\Database;
use Nest\Database\Query\QueryBuilder;
use Nest\Database\Transactions\Transaction;
use Nest\Exceptions\Database\NotCurrentTransactionException;
use Throwable;
/**
* Class of a database.
*/
abstract class Database
{
/**
* The current transaction, in one is started.
* @var Transaction|null
*/
private ?Transaction $transaction = null;
/**
* Connect to the database.
* @return void
*/
public abstract function connect(): void;
/**
* Execute a query.
* @param string $statement The query statement.
* @param array $bindings The query bindings.
* @return object[] Query result as an array of objects.
*/
public abstract function execute(string $statement, array $bindings = []): array;
/**
* Get a queries adapter for the current database.
* @return DatabaseAdapter A database queries adapter.
*/
public abstract function getQueriesAdapter(): DatabaseAdapter;
/**
* Create a new query on the database.
* @param ?string $table The table on which to build the new query.
* @return QueryBuilder A query builder.
*/
public function query(?string $table = null): QueryBuilder
{
return new QueryBuilder($this, $table);
}
/**
* Create a new query on the given table of the database.
* @param string $table The table on which to build the new query.
* @return QueryBuilder A query builder.
*/
public function table(string $table): QueryBuilder
{
return $this->query($table);
}
/**
* Get the current transaction, if there is one.
* @return Transaction|null
*/
public function getCurrentTransaction(): ?Transaction
{
return $this->transaction;
}
/**
* Called when the state of a transaction changed (inactive -> active or active -> inactive).
* @param Transaction $transaction
* @return void
* @throws NotCurrentTransactionException
*/
public function onTransactionStateChanged(Transaction $transaction): void
{
if ($transaction->isActive())
{ // Set the newly active transaction as the current one.
// Its parent should be the current transaction, if it exists.
if (!empty($transaction->getParent()) && $transaction->getParent()->getUuid() != $this->transaction->getUuid())
throw new NotCurrentTransactionException($this->transaction, $transaction->getParent());
$this->transaction = $transaction;
}
else
{ // The transaction has been terminated, it should be the current one.
if (!empty($this->transaction) && $this->transaction->getUuid() != $transaction->getUuid())
throw new NotCurrentTransactionException($this->transaction, $transaction);
$this->transaction = $transaction->getParent();
}
}
/**
* Start a new transaction (in the current one, if there is one).
* @return Transaction The new transaction.
* @throws NotCurrentTransactionException
*/
public function startTransaction(): Transaction
{
// Create the transaction as a child of the existing one, if there is one.
$newTransaction = new Transaction($this, $this->transaction);
if (!empty($this->transaction))
// Set the new transaction as child of the current one.
$this->transaction->setChild($newTransaction);
$newTransaction->start(); // Start the new transaction.
return $newTransaction; // Return the new transaction.
}
/**
* Start a new transaction and execute the function in parameter.
* The transaction will be committed / rolled back automatically at the end of the function.
* A rollback happen if any exception is thrown in the function.
* @param callable(Transaction): mixed $function The function to execute in a transaction.
* @return mixed Function result.
* @throws Throwable
*/
public function transaction(callable $function): mixed
{
// Start a new transaction.
$transaction = $this->startTransaction();
try
{ // Trying to run the function in the started transaction.
$result = $function($transaction);
// The function finished successfully, commit the transaction.
$transaction->commit();
// Return result of the function.
return $result;
}
catch (Throwable $throwable)
{ // An exception has been thrown, rolling back the transaction and throw it again.
$transaction->rollback();
throw $throwable;
}
}
}