début d'implémentation capacitor

This commit is contained in:
Jephté Clain 2024-05-18 13:09:01 +04:00
parent 52700ee53b
commit a3ae6847d4
8 changed files with 221 additions and 5 deletions

View File

@ -11,6 +11,25 @@ use SQLite3Stmt;
* Class Sqlite: frontend vers une base de données sqlite3
*/
class Sqlite {
static function with($sqlite, ?array $params=null): self {
if ($sqlite instanceof static) {
return $sqlite;
} elseif ($sqlite instanceof self) {
# recréer avec les mêmes paramètres
return new static(null, cl::merge([
"file" => $sqlite->file,
"flags" => $sqlite->flags,
"encryption_key" => $sqlite->encryptionKey,
"config" => $sqlite->config,
"migrate" => $sqlite->migration,
], $params));
} elseif (is_array($sqlite)) {
return new static(null, cl::merge($sqlite, $params));
} else {
return new static($sqlite, $params);
}
}
static function config_enableExceptions(self $sqlite) {
$sqlite->db->enableExceptions(true);
}

View File

@ -0,0 +1,137 @@
<?php
namespace nur\sery\db\sqlite;
use nur\sery\cl;
use nur\sery\php\func;
/**
* Class SqliteCapacitor: objet permettant d'accumuler des données pour les
* réutiliser plus tard
*/
class SqliteCapacitor {
function __construct($sqlite) {
$this->sqlite = Sqlite::with($sqlite);
}
/** @var Sqlite */
protected $sqlite;
protected function getTableName(?string $channel): string {
return ($channel ?? "default")."_channel";
}
/** tester si le canal spécifié existe */
function exists(?string $channel=null): bool {
}
/** supprimer le canal spécifié */
function reset(?string $channel=null) {
$tableName = $this->getTableName($channel);
$this->sqlite->exec("drop table if exists $tableName");
}
/** @var array */
protected $created;
protected function getKeyDefinitions(?string $channel): ?array {
return null;
}
protected function getKeyValues($item, ?string $channel): ?array {
return null;
}
protected function create(?string $channel): void {
if ($this->created[$channel] ?? false) return;
$columns = cl::merge([
"_id" => "integer primary key autoincrement",
"_item" => "text",
], $this->getKeyDefinitions($channel));
#XXXvvv migrer vers la syntaxe tableau de create
$index = 0;
foreach ($columns as $column => &$definition) {
if ($column === $index) {
$index++;
} else {
$definition = "$column $definition";
}
}; unset($definition);
$query = implode("", [
"create table if not exists ",
$this->getTableName($channel),
" (",
implode(", ", $columns),
")",
]);
#XXX^^^ migrer vers la syntaxe tableau de create
$this->sqlite->exec($query);
$this->created[$channel] = true;
}
function charge($item, ?string $channel=null) {
$this->create($channel);
$values = cl::merge([
"_item" => serialize($item),
], $this->getKeyValues($item, $channel));
$this->sqlite->exec([
"insert",
"into" => $this->getTableName($channel),
"values" => $values,
]);
}
/** décharger les données du canal spécifié */
function discharge(?string $channel=null, bool $reset=true): iterable {
$rows = $this->sqlite->all([
"select _item",
"from" => $this->getTableName($channel),
]);
foreach ($rows as $row) {
$item = unserialize($row['_item']);
yield $item;
}
if ($reset) $this->reset($channel);
}
/** appeler une fonction pour chaque élément du canal spécifié */
function each(callable $func, ?string $channel=null): void {
$context = func::_prepare($func);
foreach ($this->discharge($channel, false) as $item) {
func::_call($context, [$item]);
}
}
/** obtenir l'élément identifié par les clés spécifiées sur le canal spécifié */
function get(?array $keys, ?string $channel=null) {
}
function close(): void {
$this->sqlite->close();
}
function sqlite__exec(string $query): bool {
return $this->sqlite->_exec($query);
}
function sqlite_exec($query, ?array $params=null): bool {
return $this->sqlite->exec($query, $params);
}
function sqlite__get(string $query, bool $entireRow=false) {
return $this->sqlite->_get($query, $entireRow);
}
function sqlite_get($query, ?array $params=null, bool $entireRow=false) {
return $this->sqlite->get($query, $params, $entireRow);
}
function sqlite_one($query, ?array $params=null): ?array {
return $this->sqlite->one($query, $params);
}
function sqlite_all($query, ?array $params=null): iterable {
return $this->sqlite->all($query, $params);
}
}

View File

@ -5,8 +5,13 @@ use nur\sery\php\func;
class _migration {
static function with($migrations): self {
if ($migrations instanceof static) return $migrations;
return new static($migrations);
if ($migrations instanceof static) {
return $migrations;
} elseif ($migrations instanceof self) {
return new static($migrations->migrations);
} else {
return new static($migrations);
}
}
const MIGRATE = null;

View File

@ -60,7 +60,7 @@ class _query {
return true;
}
protected static function parse_conds(?array $conds, ?array &$sql, ?array &$params): void {
static function parse_conds(?array $conds, ?array &$sql, ?array &$params): void {
if (!$conds) return;
$sep = null;
$index = 0;
@ -126,7 +126,7 @@ class _query {
}
}
protected static function parse_set_values(?array $values, ?array &$sql, ?array &$params): void {
static function parse_set_values(?array $values, ?array &$sql, ?array &$params): void {
if (!$values) return;
$index = 0;
$parts = [];

1
tests/db/sqlite/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/capacitor.db

View File

@ -0,0 +1,48 @@
<?php
namespace nur\sery\db\sqlite;
use PHPUnit\Framework\TestCase;
class SqliteCapacitorTest extends TestCase {
function _testChargeStrings(SqliteCapacitor $capacitor, ?string $channel) {
$capacitor->reset($channel);
$capacitor->charge("first", $channel);
$capacitor->charge("second", $channel);
$capacitor->charge("third", $channel);
$items = iterator_to_array($capacitor->discharge($channel, false));
self::assertSame(["first", "second", "third"], $items);
}
function _testChargeArrays(SqliteCapacitor $capacitor, ?string $channel) {
$capacitor->reset($channel);
$capacitor->charge(["id" => 10, "name" => "first"], $channel);
$capacitor->charge(["name" => "second", "id" => 20], $channel);
$capacitor->charge(["name" => "third", "id" => "30"], $channel);
}
function testChargeStrings() {
$capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
$this->_testChargeStrings($capacitor, null);
$capacitor->close();
}
function testChargeArrays() {
$capacitor = new class(__DIR__.'/capacitor.db') extends SqliteCapacitor {
protected function getKeyDefinitions(?string $channel): ?array {
if ($channel === "arrays") {
return ["id" => "integer"];
}
return parent::getKeyDefinitions($channel);
}
protected function getKeyValues($item, ?string $channel): ?array {
if ($channel === "arrays") {
return ["id" => $item["id"] ?? null];
}
return parent::getKeyValues($item, $channel);
}
};
$this->_testChargeStrings($capacitor, "strings");
$this->_testChargeArrays($capacitor, "arrays");
$capacitor->close();
}
}

View File

@ -41,6 +41,12 @@ class SqliteTest extends TestCase {
self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]);
}
protected function assertInserted(Sqlite $sqlite, array $row, array $query): void {
$sqlite->exec($query);
self::assertSame($row, $sqlite->one("select * from mapping where i = :i", [
"i" => $query["values"]["i"],
]));
}
function testInsert() {
$sqlite = new Sqlite(":memory:", [
"migrate" => "create table mapping (i integer, s varchar)",

View File

@ -3,7 +3,7 @@ namespace nur\sery\db\sqlite;
use PHPUnit\Framework\TestCase;
class _QueryTest extends TestCase {
class _queryTest extends TestCase {
function testParseConds(): void {
$sql = $params = null;
_query::parse_conds(null, $sql, $params);