modifs.mineures sans commentaires

This commit is contained in:
Jephté Clain 2024-06-05 06:17:04 +04:00
parent 604d9c1fe3
commit fdf19326b0
6 changed files with 146 additions and 185 deletions

View File

@ -12,7 +12,7 @@ class CapacitorChannel {
const TABLE_NAME = null; const TABLE_NAME = null;
const KEY_DEFINITIONS = null; const COLUMN_DEFINITIONS = null;
const PRIMARY_KEYS = null; const PRIMARY_KEYS = null;
@ -27,11 +27,11 @@ class CapacitorChannel {
$this->name = self::verifix_name($name ?? static::NAME); $this->name = self::verifix_name($name ?? static::NAME);
$this->eachCommitThreshold = $eachCommitThreshold ?? static::EACH_COMMIT_THRESHOLD; $this->eachCommitThreshold = $eachCommitThreshold ?? static::EACH_COMMIT_THRESHOLD;
$this->created = false; $this->created = false;
$keyDefinitions = cl::withn(static::KEY_DEFINITIONS); $columnDefinitions = cl::withn(static::COLUMN_DEFINITIONS);
$primaryKeys = cl::withn(static::PRIMARY_KEYS); $primaryKeys = cl::withn(static::PRIMARY_KEYS);
if ($primaryKeys === null && $keyDefinitions !== null) { if ($primaryKeys === null && $columnDefinitions !== null) {
$index = 0; $index = 0;
foreach ($keyDefinitions as $col => $def) { foreach ($columnDefinitions as $col => $def) {
if ($col == $index) { if ($col == $index) {
$index++; $index++;
if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) { if (preg_match('/\bprimary\s+key\s+\((.+)\)/i', $def, $ms)) {
@ -44,7 +44,7 @@ class CapacitorChannel {
} }
} }
} }
$this->keyDefinitions = $keyDefinitions; $this->columnDefinitions = $columnDefinitions;
$this->primaryKeys = $primaryKeys; $this->primaryKeys = $primaryKeys;
} }
@ -80,7 +80,7 @@ class CapacitorChannel {
$this->created = $created; $this->created = $created;
} }
protected ?array $keyDefinitions; protected ?array $columnDefinitions;
/** /**
* retourner un ensemble de définitions pour des colonnes supplémentaires à * retourner un ensemble de définitions pour des colonnes supplémentaires à
@ -98,8 +98,8 @@ class CapacitorChannel {
* lors de l'insertion dans la base de données, et automatiquement désérialisées * lors de l'insertion dans la base de données, et automatiquement désérialisées
* avant d'être retournées à l'utilisateur (sans le suffixe "__") * avant d'être retournées à l'utilisateur (sans le suffixe "__")
*/ */
function getKeyDefinitions(): ?array { function getColumnDefinitions(): ?array {
return $this->keyDefinitions; return $this->columnDefinitions;
} }
protected ?array $primaryKeys; protected ?array $primaryKeys;
@ -122,6 +122,9 @@ class CapacitorChannel {
/** /**
* Avant d'utiliser un id pour rechercher dans la base de donnée, corriger sa * Avant d'utiliser un id pour rechercher dans la base de donnée, corriger sa
* valeur le cas échéant. * valeur le cas échéant.
*
* Cette fonction assume que l'unique clé primaire est id_. Elle n'est pas
* utilisée si une clé primaire multiple est définie.
*/ */
function verifixId(string &$id): void { function verifixId(string &$id): void {
} }

View File

@ -3,13 +3,112 @@ namespace nur\sery\db;
use nur\sery\cl; use nur\sery\cl;
use nur\sery\php\func; use nur\sery\php\func;
use nur\sery\str;
/** /**
* Class CapacitorStorage: objet permettant d'accumuler des données pour les * Class CapacitorStorage: objet permettant d'accumuler des données pour les
* réutiliser plus tard * réutiliser plus tard
*/ */
abstract class CapacitorStorage { abstract class CapacitorStorage {
abstract protected function getChannel(?string $name): CapacitorChannel; /** @var CapacitorChannel[] */
protected $channels;
function addChannel(CapacitorChannel $channel): CapacitorChannel {
$this->_create($channel);
$this->channels[$channel->getName()] = $channel;
return $channel;
}
protected function getChannel(?string $name): CapacitorChannel {
$name = CapacitorChannel::verifix_name($name);
$channel = $this->channels[$name] ?? null;
if ($channel === null) {
$channel = $this->addChannel(new CapacitorChannel($name));
}
return $channel;
}
const PRIMARY_KEY_DEFINITION = [
"id_" => "integer primary key autoincrement",
];
const COLUMN_DEFINITIONS = [
"item__" => "text",
"sum_" => "varchar(40)",
"created_" => "datetime",
"modified_" => "datetime",
];
protected function ColumnDefinitions(CapacitorChannel $channel): array {
$definitions = [];
if ($channel->getPrimaryKeys() === null) {
$definitions[] = self::PRIMARY_KEY_DEFINITION;
}
$definitions[] = self::COLUMN_DEFINITIONS;
$definitions[] = $channel->getColumnDefinitions();
return cl::merge(...$definitions);
}
/** sérialiser les valeurs qui doivent l'être dans $values */
protected function serialize(CapacitorChannel $channel, ?array $values): ?array {
if ($values === null) return null;
$cols = $this->ColumnDefinitions($channel);
$index = 0;
$row = [];
foreach (array_keys($cols) as $col) {
$key = $col;
if ($key === $index) {
$index++;
continue;
} elseif (str::del_suffix($key, "__")) {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
if ($value !== null) $value = serialize($value);
} else {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
}
$row[$col] = $value;
}
return $row;
}
/** désérialiser les valeurs qui doivent l'être dans $values */
protected function unserialize(CapacitorChannel $channel, ?array $row): ?array {
if ($row === null) return null;
$cols = $this->ColumnDefinitions($channel);
$index = 0;
$values = [];
foreach (array_keys($cols) as $col) {
$key = $col;
if ($key === $index) {
$index++;
continue;
} elseif (!array_key_exists($col, $row)) {
continue;
} elseif (str::del_suffix($key, "__")) {
$value = $row[$col];
if ($value !== null) $value = unserialize($value);
} else {
$value = $row[$col];
}
$values[$key] = $value;
}
return $values;
}
protected function getPrimaryKeys(CapacitorChannel $channel): array {
$primaryKeys = $channel->getPrimaryKeys();
if ($primaryKeys === null) $primaryKeys = ["id_"];
return $primaryKeys;
}
protected function getRowIds(CapacitorChannel $channel, ?array $row, ?array &$primaryKeys=null): ?array {
$primaryKeys = $this->getPrimaryKeys($channel);
$rowIds = cl::select($row, $primaryKeys);
if (cl::all_n($rowIds)) return null;
else return $rowIds;
}
abstract function _exists(CapacitorChannel $channel): bool; abstract function _exists(CapacitorChannel $channel): bool;
@ -75,12 +174,6 @@ abstract class CapacitorStorage {
return $this->_one($this->getChannel($channel), $filter); return $this->_one($this->getChannel($channel), $filter);
} }
protected function getPrimaryKeys(CapacitorChannel $channel): array {
$primaryKeys = $channel->getPrimaryKeys();
if ($primaryKeys === null) $primaryKeys = ["id_"];
return $primaryKeys;
}
abstract function _all(CapacitorChannel $channel, $filter): iterable; abstract function _all(CapacitorChannel $channel, $filter): iterable;
/** /**

View File

@ -23,92 +23,18 @@ class MysqlStorage extends CapacitorStorage {
return $this->mysql; return $this->mysql;
} }
const KEY_DEFINITIONS = [
"id_" => "integer primary key autoincrement",
"item__" => "text",
"sum_" => "varchar(40)",
"created_" => "datetime",
"modified_" => "datetime",
];
/** sérialiser les valeurs qui doivent l'être dans $values */
protected function serialize(CapacitorChannel $channel, ?array $values): ?array {
if ($values === null) return null;
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions());
$index = 0;
$row = [];
foreach (array_keys($columns) as $column) {
$key = $column;
if ($key === $index) {
$index++;
continue;
} elseif (str::del_suffix($key, "__")) {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
if ($value !== null) $value = serialize($value);
} else {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
}
$row[$column] = $value;
}
return $row;
}
/** désérialiser les valeurs qui doivent l'être dans $values */
protected function unserialize(CapacitorChannel $channel, ?array $row): ?array {
if ($row === null) return null;
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions());
$index = 0;
$values = [];
foreach (array_keys($columns) as $column) {
$key = $column;
if ($key === $index) {
$index++;
continue;
} elseif (!array_key_exists($column, $row)) {
continue;
} elseif (str::del_suffix($key, "__")) {
$value = $row[$column];
if ($value !== null) $value = unserialize($value);
} else {
$value = $row[$column];
}
$values[$key] = $value;
}
return $values;
}
protected function _create(CapacitorChannel $channel): void { protected function _create(CapacitorChannel $channel): void {
if (!$channel->isCreated()) { if (!$channel->isCreated()) {
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions()); $cols = $this->ColumnDefinitions($channel);
$this->mysql->exec([ $this->mysql->exec([
"create table if not exists", "create table if not exists",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"cols" => $columns, "cols" => $cols,
]); ]);
$channel->setCreated(); $channel->setCreated();
} }
} }
/** @var CapacitorChannel[] */
protected $channels;
function addChannel(CapacitorChannel $channel): CapacitorChannel {
$this->_create($channel);
$this->channels[$channel->getName()] = $channel;
return $channel;
}
protected function getChannel(?string $name): CapacitorChannel {
$name = CapacitorChannel::verifix_name($name);
$channel = $this->channels[$name] ?? null;
if ($channel === null) {
$channel = $this->addChannel(new CapacitorChannel($name));
}
return $channel;
}
function _exists(CapacitorChannel $channel): bool { function _exists(CapacitorChannel $channel): bool {
$tableName = $this->mysql->get([ $tableName = $this->mysql->get([
"select table_name from information_schema.tables", "select table_name from information_schema.tables",
@ -142,13 +68,19 @@ class MysqlStorage extends CapacitorStorage {
"sum_" => $sum_, "sum_" => $sum_,
], $this->unserialize($channel, $channel->getKeyValues($item))); ], $this->unserialize($channel, $channel->getKeyValues($item)));
$prow = null; $prow = null;
$id_ = $row["id_"] ?? null; $rowIds = $this->getRowIds($channel, $row, $primaryKeys);
if ($id_ !== null) { if ($rowIds !== null) {
# modification # modification
$prow = $this->mysql->one([ $prow = $this->mysql->one([
"select id_, item__, sum_, created_, modified_", "select",
"cols" => array_merge($primaryKeys, [
"item__",
"sum_",
"created_",
"modified_",
]),
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => ["id_" => $id_], "where" => $rowIds,
]); ]);
} }
$insert = null; $insert = null;
@ -199,7 +131,7 @@ class MysqlStorage extends CapacitorStorage {
"update", "update",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"values" => $row, "values" => $row,
"where" => ["id_" => $id_], "where" => $rowIds,
]); ]);
} }
return 1; return 1;
@ -269,6 +201,7 @@ class MysqlStorage extends CapacitorStorage {
try { try {
$args ??= []; $args ??= [];
foreach ($this->_all($channel, $filter) as $row) { foreach ($this->_all($channel, $filter) as $row) {
$rowIds = $this->getRowIds($channel, $row);
$updates = func::_call($onEach, [$row["item"], $row, ...$args]); $updates = func::_call($onEach, [$row["item"], $row, ...$args]);
if (is_array($updates)) { if (is_array($updates)) {
$updates = $this->serialize($channel, $updates); $updates = $this->serialize($channel, $updates);
@ -283,7 +216,7 @@ class MysqlStorage extends CapacitorStorage {
"update", "update",
"table" => $tableName, "table" => $tableName,
"values" => $updates, "values" => $updates,
"where" => ["id_" => $row["id_"]], "where" => $rowIds,
]); ]);
if ($commitThreshold !== null) { if ($commitThreshold !== null) {
$commitThreshold--; $commitThreshold--;

View File

@ -5,7 +5,6 @@ use nur\sery\cl;
use nur\sery\db\CapacitorChannel; use nur\sery\db\CapacitorChannel;
use nur\sery\db\CapacitorStorage; use nur\sery\db\CapacitorStorage;
use nur\sery\php\func; use nur\sery\php\func;
use nur\sery\str;
use nur\sery\ValueException; use nur\sery\ValueException;
/** /**
@ -23,92 +22,18 @@ class SqliteStorage extends CapacitorStorage {
return $this->sqlite; return $this->sqlite;
} }
const KEY_DEFINITIONS = [
"id_" => "integer primary key autoincrement",
"item__" => "text",
"sum_" => "varchar(40)",
"created_" => "datetime",
"modified_" => "datetime",
];
/** sérialiser les valeurs qui doivent l'être dans $values */
protected function serialize(CapacitorChannel $channel, ?array $values): ?array {
if ($values === null) return null;
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions());
$index = 0;
$row = [];
foreach (array_keys($columns) as $column) {
$key = $column;
if ($key === $index) {
$index++;
continue;
} elseif (str::del_suffix($key, "__")) {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
if ($value !== null) $value = serialize($value);
} else {
if (!array_key_exists($key, $values)) continue;
$value = $values[$key];
}
$row[$column] = $value;
}
return $row;
}
/** désérialiser les valeurs qui doivent l'être dans $values */
protected function unserialize(CapacitorChannel $channel, ?array $row): ?array {
if ($row === null) return null;
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions());
$index = 0;
$values = [];
foreach (array_keys($columns) as $column) {
$key = $column;
if ($key === $index) {
$index++;
continue;
} elseif (!array_key_exists($column, $row)) {
continue;
} elseif (str::del_suffix($key, "__")) {
$value = $row[$column];
if ($value !== null) $value = unserialize($value);
} else {
$value = $row[$column];
}
$values[$key] = $value;
}
return $values;
}
protected function _create(CapacitorChannel $channel): void { protected function _create(CapacitorChannel $channel): void {
if (!$channel->isCreated()) { if (!$channel->isCreated()) {
$columns = cl::merge(self::KEY_DEFINITIONS, $channel->getKeyDefinitions()); $cols = $this->ColumnDefinitions($channel);
$this->sqlite->exec([ $this->sqlite->exec([
"create table if not exists", "create table if not exists",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"cols" => $columns, "cols" => $cols,
]); ]);
$channel->setCreated(); $channel->setCreated();
} }
} }
/** @var CapacitorChannel[] */
protected $channels;
function addChannel(CapacitorChannel $channel): CapacitorChannel {
$this->_create($channel);
$this->channels[$channel->getName()] = $channel;
return $channel;
}
protected function getChannel(?string $name): CapacitorChannel {
$name = CapacitorChannel::verifix_name($name);
$channel = $this->channels[$name] ?? null;
if ($channel === null) {
$channel = $this->addChannel(new CapacitorChannel($name));
}
return $channel;
}
function _exists(CapacitorChannel $channel): bool { function _exists(CapacitorChannel $channel): bool {
$tableName = $this->sqlite->get([ $tableName = $this->sqlite->get([
"select name from sqlite_schema", "select name from sqlite_schema",
@ -141,13 +66,19 @@ class SqliteStorage extends CapacitorStorage {
"sum_" => $sum_, "sum_" => $sum_,
], $this->unserialize($channel, $channel->getKeyValues($item))); ], $this->unserialize($channel, $channel->getKeyValues($item)));
$prow = null; $prow = null;
$id_ = $row["id_"] ?? null; $rowIds = $this->getRowIds($channel, $row, $primaryKeys);
if ($id_ !== null) { if ($rowIds !== null) {
# modification # modification
$prow = $this->sqlite->one([ $prow = $this->sqlite->one([
"select id_, item__, sum_, created_, modified_", "select",
"cols" => array_merge($primaryKeys, [
"item__",
"sum_",
"created_",
"modified_",
]),
"from" => $channel->getTableName(), "from" => $channel->getTableName(),
"where" => ["id_" => $id_], "where" => $rowIds,
]); ]);
} }
$insert = null; $insert = null;
@ -198,7 +129,7 @@ class SqliteStorage extends CapacitorStorage {
"update", "update",
"table" => $channel->getTableName(), "table" => $channel->getTableName(),
"values" => $row, "values" => $row,
"where" => ["id_" => $id_], "where" => $rowIds,
]); ]);
} }
return 1; return 1;
@ -268,6 +199,7 @@ class SqliteStorage extends CapacitorStorage {
try { try {
$args ??= []; $args ??= [];
foreach ($this->_all($channel, $filter) as $row) { foreach ($this->_all($channel, $filter) as $row) {
$rowIds = $this->getRowIds($channel, $row);
$updates = func::_call($onEach, [$row["item"], $row, ...$args]); $updates = func::_call($onEach, [$row["item"], $row, ...$args]);
if (is_array($updates)) { if (is_array($updates)) {
$updates = $this->serialize($channel, $updates); $updates = $this->serialize($channel, $updates);
@ -282,7 +214,7 @@ class SqliteStorage extends CapacitorStorage {
"update", "update",
"table" => $tableName, "table" => $tableName,
"values" => $updates, "values" => $updates,
"where" => ["id_" => $row["id_"]], "where" => $rowIds,
]); ]);
if ($commitThreshold !== null) { if ($commitThreshold !== null) {
$commitThreshold--; $commitThreshold--;

View File

@ -32,7 +32,7 @@ class SqliteStorageTest extends TestCase {
$storage = new SqliteStorage(__DIR__.'/capacitor.db'); $storage = new SqliteStorage(__DIR__.'/capacitor.db');
$storage->addChannel(new class extends CapacitorChannel { $storage->addChannel(new class extends CapacitorChannel {
const NAME = "arrays"; const NAME = "arrays";
function getKeyDefinitions(): ?array { function getColumnDefinitions(): ?array {
return ["id" => "integer"]; return ["id" => "integer"];
} }
function getKeyValues($item): ?array { function getKeyValues($item): ?array {
@ -50,7 +50,7 @@ class SqliteStorageTest extends TestCase {
$capacitor = new Capacitor($storage, new class extends CapacitorChannel { $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "each"; const NAME = "each";
function getKeyDefinitions(): ?array { function getColumnDefinitions(): ?array {
return [ return [
"age" => "integer", "age" => "integer",
"done" => "integer default 0", "done" => "integer default 0",
@ -90,7 +90,7 @@ class SqliteStorageTest extends TestCase {
$capacitor = new Capacitor($storage, new class extends CapacitorChannel { $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
const NAME = "pk"; const NAME = "pk";
function getKeyDefinitions(): ?array { function getColumnDefinitions(): ?array {
return [ return [
"id_" => "varchar primary key", "id_" => "varchar primary key",
"done" => "integer default 0", "done" => "integer default 0",

View File

@ -99,7 +99,7 @@ class _queryTest extends TestCase {
$sql = $params = null; $sql = $params = null;
_query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params); _query_base::parse_set_values([["int" => 42, "string" => "value"], ["int" => 24, "string" => "eulav"]], $sql, $params);
self::assertSame(["int = :int", "string = :string", "int = :int2", "string = :string2"], $sql); self::assertSame(["int = :int", "string = :string", "int = :int1", "string = :string1"], $sql);
self::assertSame(["int" => 42, "string" => "value", "int2" => 24, "string2" => "eulav"], $params); self::assertSame(["int" => 42, "string" => "value", "int1" => 24, "string1" => "eulav"], $params);
} }
} }