204 lines
6.4 KiB
PHP
204 lines
6.4 KiB
PHP
<?php
|
|
namespace nur\sery\db;
|
|
|
|
use nur\sery\cl;
|
|
use nur\sery\str;
|
|
|
|
/**
|
|
* Class CapacitorStorage: objet permettant d'accumuler des données pour les
|
|
* réutiliser plus tard
|
|
*/
|
|
abstract class CapacitorStorage {
|
|
/** @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;
|
|
}
|
|
|
|
/** DOIT être défini dans les classes dérivées */
|
|
const PRIMARY_KEY_DEFINITION = null;
|
|
|
|
const COLUMN_DEFINITIONS = [
|
|
"item__" => "text",
|
|
"sum_" => "varchar(40)",
|
|
"created_" => "datetime",
|
|
"modified_" => "datetime",
|
|
];
|
|
|
|
protected function ColumnDefinitions(CapacitorChannel $channel): array {
|
|
$definitions = [];
|
|
if ($channel->getPrimaryKeys() === null) {
|
|
$definitions[] = static::PRIMARY_KEY_DEFINITION;
|
|
}
|
|
$definitions[] = static::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;
|
|
|
|
/** tester si le canal spécifié existe */
|
|
function exists(?string $channel): bool {
|
|
return $this->_exists($this->getChannel($channel));
|
|
}
|
|
|
|
abstract function _ensureExists(CapacitorChannel $channel): void;
|
|
|
|
/** s'assurer que le canal spécifié existe */
|
|
function ensureExists(?string $channel): void {
|
|
$this->_ensureExists($this->getChannel($channel));
|
|
}
|
|
|
|
abstract function _reset(CapacitorChannel $channel): void;
|
|
|
|
/** supprimer le canal spécifié */
|
|
function reset(?string $channel): void {
|
|
$this->_reset($this->getChannel($channel));
|
|
}
|
|
|
|
abstract function _charge(CapacitorChannel $channel, $item, $func, ?array $args): int;
|
|
|
|
/**
|
|
* charger une valeur dans le canal
|
|
*
|
|
* Si $func!==null, après avoir calculé les valeurs des clés supplémentaires
|
|
* avec {@link CapacitorChannel::getItemValues()}, la fonction est appelée avec
|
|
* la signature ($item, $keyValues, $row, ...$args)
|
|
* Si la fonction retourne un tableau, il est utilisé pour modifier les valeurs
|
|
* insérées/mises à jour
|
|
*
|
|
* @return int 1 si l'objet a été chargé ou mis à jour, 0 s'il existait
|
|
* déjà à l'identique dans le canal
|
|
*/
|
|
function charge(?string $channel, $item, $func=null, ?array $args=null): int {
|
|
return $this->_charge($this->getChannel($channel), $item, $func, $args);
|
|
}
|
|
|
|
abstract function _discharge(CapacitorChannel $channel, bool $reset=true): iterable;
|
|
|
|
/** décharger les données du canal spécifié */
|
|
function discharge(?string $channel, bool $reset=true): iterable {
|
|
return $this->_discharge($this->getChannel($channel), $reset);
|
|
}
|
|
|
|
abstract function _count(CapacitorChannel $channel, $filter): int;
|
|
|
|
/** indiquer le nombre d'éléments du canal spécifié */
|
|
function count(?string $channel, $filter=null): int {
|
|
return $this->_count($this->getChannel($channel), $filter);
|
|
}
|
|
|
|
abstract function _one(CapacitorChannel $channel, $filter): ?array;
|
|
|
|
/**
|
|
* obtenir la ligne correspondant au filtre sur le canal spécifié
|
|
*
|
|
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
|
|
*/
|
|
function one(?string $channel, $filter): ?array {
|
|
return $this->_one($this->getChannel($channel), $filter);
|
|
}
|
|
|
|
abstract function _all(CapacitorChannel $channel, $filter): iterable;
|
|
|
|
/**
|
|
* obtenir les lignes correspondant au filtre sur le canal spécifié
|
|
*
|
|
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
|
|
*/
|
|
function all(?string $channel, $filter): iterable {
|
|
return $this->_all($this->getChannel($channel), $filter);
|
|
}
|
|
|
|
abstract function _each(CapacitorChannel $channel, $filter, $func, ?array $args): int;
|
|
|
|
/**
|
|
* appeler une fonction pour chaque élément du canal spécifié.
|
|
*
|
|
* $filter permet de filtrer parmi les élements chargés
|
|
*
|
|
* $func est appelé avec la signature ($item, $row, ...$args). si la fonction
|
|
* retourne un tableau, il est utilisé pour mettre à jour la ligne
|
|
*
|
|
* @return int le nombre de lignes parcourues
|
|
*/
|
|
function each(?string $channel, $filter, $func=null, ?array $args=null): int {
|
|
return $this->_each($this->getChannel($channel), $filter, $func, $args);
|
|
}
|
|
|
|
abstract function close(): void;
|
|
}
|