Skip to content

Instantly share code, notes, and snippets.

@someniatko
Last active May 5, 2020 19:05
Show Gist options
  • Save someniatko/ae8ac64419d7ce28e1b092384714b404 to your computer and use it in GitHub Desktop.
Save someniatko/ae8ac64419d7ce28e1b092384714b404 to your computer and use it in GitHub Desktop.
DBAL wrapper draft
<?php
use MyCLabs\Enum\Enum;
use Doctrine\DBAL\Driver\Connection;
interface QueryExecutorInterface
{
/**
* @psalm-template T
* @psalm-param Query<T>
* @psalm-return T
*/
public function execute(Query $query);
}
/**
* @method static self ALL_ROWS()
* @method static self ROW()
* @method static self COLUMN()
* @method static self NOTHING()
* @psalm-immutable
* @psalm-extends Enum<int>
*/
final class FetchScope extends Enum
{
public const ALL_ROWS = 1;
public const COLUMN = 2;
public const ROW = 3;
public const NOTHING = 4;
}
/**
* @psalm-immutable
* @psalm-template T
*/
final class Query
{
public string $sql;
/** @var array<string, mixed> */
public array $params;
public FetchScope $scope;
public int $fetchMode;
public ?string $fetchModeParam;
/** @param array<string, mixed> $params */
public function __construct(
string $sql,
array $params,
FetchScope $scope,
int $fetchMode = FetchMode::MIXED,
?string $fetchModeParam = null
) {
$this->sql = $sql;
$this->params = $params;
$this->scope = $scope;
$this->fetchMode = $fetchMode;
$this->fetchModeParam = $fetchModeParam;
}
}
final class QueryExecutor implements QueryExecutorInterface
{
private Connection $dbConnection;
public function __construct(Connection $dbConnection)
{
$this->dbConnection = $dbConnection;
}
public function execute(Query $query)
{
$statement = $this->dbConnection->prepare($query->sql);
foreach ($query->params as $paramName => $paramValue) {
$statement->bindValue($paramName, $paramValue);
}
$statement->setFetchMode($query->fetchMode, $query->fetchModeParam);
$statement->execute();
switch ($query->scope->getValue()) {
case FetchScope::ALL_ROWS:
return $statement->fetchAll();
case FetchScope::ROW:
return $statement->fetch();
case FetchScope::COLUMN:
return $statement->fetchColumn();
case FetchScope::NOTHING:
return null;
}
throw new \LogicException('should never happen');
}
}
<?php
/**
* @psalm-immutable
*/
interface LogQueryBuilderInterface
{
/** @return Query<LogDbRecord> */
public function selectLog(string $id, string $type): Query;
/** @return Query<null> */
public function insertOrUpdateLog(LogDbRecord $record): Query;
/** @psalm-return Query<list<LogDbRecord>> */
public function selectLogsChunkByType(string $type, int $limit, int $offset): Query;
/** @return Query<int> */
public function countLogsByType(string $type): Query;
}
/**
* @psalm-immutable
*/
interface LogDbRecordMapperInterface
{
public function toDbRecord(ImportType $type, ImportLog $log): LogDbRecord;
public function fromDbRecord(LogDbRecord $record): ImportLog;
}
final class PersistenceLayer implements PersistenceLayerInterface
{
private QueryExecutorInterface $executor;
private LogQueryBuilderInterface $queries;
private LogDbRecordMapperInterface $mapper;
public function __construct(
QueryExecutorInterface $executor,
LogQueryBuilderInterface $queries,
LogDbRecordMapperInterface $mapper
) {
$this->executor = $executor;
$this->queries = $queries;
$this->mapper = $mapper;
}
public function readLog(ImportType $type, LogId $id): ImportLog
{
return $this->mapper->fromDbRecord(
$this->executor->execute(
$this->queries->selectLog(
$id->value->getBytes(),
$type->getValue()
)
)
);
}
public function saveLog(ImportType $type, ImportLog $log): void
{
$this->executor->execute(
$this->queries->insertOrUpdateLog(
$this->mapper->toDbRecord($type, $log)
)
);
}
public function readLogsChunk(ImportType $type, ChunkSpec $chunkSpec): array
{
return $this->executor->execute(
$this->queries->selectLogsChunkByType((string) $type, $chunkSpec->limit, $chunkSpec->offset)
);
}
public function countLogs(ImportType $type): int
{
return $this->executor->execute(
$this->queries->countLogsByType((string) $type)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment