212 lines
6.0 KiB
PHP
212 lines
6.0 KiB
PHP
<?php
|
|
namespace nulib\db;
|
|
|
|
use nulib\cl;
|
|
use nulib\php\func;
|
|
use nulib\ValueException;
|
|
use Traversable;
|
|
|
|
/**
|
|
* Class Capacitor: un objet permettant d'attaquer un canal spécifique d'une
|
|
* instance de {@link CapacitorStorage}
|
|
*/
|
|
class Capacitor implements ITransactor {
|
|
function __construct(CapacitorStorage $storage, CapacitorChannel $channel, bool $ensureExists=true) {
|
|
$this->storage = $storage;
|
|
$this->channel = $channel;
|
|
$this->channel->setCapacitor($this);
|
|
if ($ensureExists) $this->ensureExists();
|
|
}
|
|
|
|
/** @var CapacitorStorage */
|
|
protected $storage;
|
|
|
|
function getStorage(): CapacitorStorage {
|
|
return $this->storage;
|
|
}
|
|
|
|
function db(): IDatabase {
|
|
return $this->getStorage()->db();
|
|
}
|
|
|
|
/** @var CapacitorChannel */
|
|
protected $channel;
|
|
|
|
function getChannel(): CapacitorChannel {
|
|
return $this->channel;
|
|
}
|
|
|
|
function getTableName(): string {
|
|
return $this->getChannel()->getTableName();
|
|
}
|
|
|
|
function getCreateSql(): string {
|
|
$channel = $this->channel;
|
|
return $this->storage->_getMigration($channel)->getSql(get_class($channel), $this->db());
|
|
}
|
|
|
|
/** @var CapacitorChannel[] */
|
|
protected ?array $subChannels = null;
|
|
|
|
protected ?array $subManageTransactions = null;
|
|
|
|
function willUpdate(...$channels): self {
|
|
if ($this->subChannels === null) {
|
|
# désactiver la gestion des transaction sur le channel local aussi
|
|
$this->subChannels[] = $this->channel;
|
|
}
|
|
if ($channels) {
|
|
foreach ($channels as $channel) {
|
|
if ($channel instanceof Capacitor) $channel = $channel->getChannel();
|
|
if ($channel instanceof CapacitorChannel) {
|
|
$this->subChannels[] = $channel;
|
|
} else {
|
|
throw ValueException::invalid_type($channel, CapacitorChannel::class);
|
|
}
|
|
}
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
function inTransaction(): bool {
|
|
return $this->db()->inTransaction();
|
|
}
|
|
|
|
function beginTransaction(?callable $func=null, bool $commit=true): void {
|
|
$db = $this->db();
|
|
if ($this->subChannels !== null) {
|
|
# on gère des subchannels: ne débuter la transaction que si ce n'est déjà fait
|
|
if ($this->subManageTransactions === null) {
|
|
foreach ($this->subChannels as $channel) {
|
|
$name = $channel->getName();
|
|
$this->subManageTransactions ??= [];
|
|
if (!array_key_exists($name, $this->subManageTransactions)) {
|
|
$this->subManageTransactions[$name] = $channel->isManageTransactions();
|
|
}
|
|
$channel->setManageTransactions(false);
|
|
}
|
|
if (!$db->inTransaction()) $db->beginTransaction();
|
|
}
|
|
} elseif (!$db->inTransaction()) {
|
|
$db->beginTransaction();
|
|
}
|
|
if ($func !== null) {
|
|
$commited = false;
|
|
try {
|
|
func::call($func, $this);
|
|
if ($commit) {
|
|
$this->commit();
|
|
$commited = true;
|
|
}
|
|
} finally {
|
|
if ($commit && !$commited) $this->rollback();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function beforeEndTransaction(): void {
|
|
if ($this->subManageTransactions !== null) {
|
|
foreach ($this->subChannels as $channel) {
|
|
$name = $channel->getName();
|
|
$channel->setManageTransactions($this->subManageTransactions[$name]);
|
|
}
|
|
$this->subManageTransactions = null;
|
|
}
|
|
}
|
|
|
|
function commit(): void {
|
|
$this->beforeEndTransaction();
|
|
$db = $this->db();
|
|
if ($db->inTransaction()) $db->commit();
|
|
}
|
|
|
|
function rollback(): void {
|
|
$this->beforeEndTransaction();
|
|
$db = $this->db();
|
|
if ($db->inTransaction()) $db->rollback();
|
|
}
|
|
|
|
function exists(): bool {
|
|
return $this->storage->_exists($this->channel);
|
|
}
|
|
|
|
function ensureExists(): void {
|
|
$this->storage->_ensureExists($this->channel);
|
|
}
|
|
|
|
function reset(bool $recreate=false): void {
|
|
$this->storage->_reset($this->channel, $recreate);
|
|
}
|
|
|
|
function charge($item, $func=null, ?array $args=null, ?array &$row=null): int {
|
|
if ($this->subChannels !== null) $this->beginTransaction();
|
|
return $this->storage->_charge($this->channel, $item, $func, $args, $row);
|
|
}
|
|
|
|
function chargeAll(?iterable $items, $func=null, ?array $args=null): int {
|
|
$count = 0;
|
|
if ($items !== null) {
|
|
if ($func !== null) {
|
|
$func = func::with($func, $args)->bind($this->channel);
|
|
}
|
|
foreach ($items as $item) {
|
|
$count += $this->charge($item, $func);
|
|
}
|
|
}
|
|
return $count;
|
|
}
|
|
|
|
function discharge(bool $reset=true): Traversable {
|
|
return $this->storage->_discharge($this->channel, $reset);
|
|
}
|
|
|
|
function count($filter=null): int {
|
|
return $this->storage->_count($this->channel, $filter);
|
|
}
|
|
|
|
function one($filter, ?array $mergeQuery=null): ?array {
|
|
return $this->storage->_one($this->channel, $filter, $mergeQuery);
|
|
}
|
|
|
|
function all($filter, ?array $mergeQuery=null): Traversable {
|
|
return $this->storage->_all($this->channel, $filter, $mergeQuery);
|
|
}
|
|
|
|
function each($filter, $func=null, ?array $args=null, ?array $mergeQuery=null, ?int &$nbUpdated=null): int {
|
|
if ($this->subChannels !== null) $this->beginTransaction();
|
|
return $this->storage->_each($this->channel, $filter, $func, $args, $mergeQuery, $nbUpdated);
|
|
}
|
|
|
|
function delete($filter, $func=null, ?array $args=null): int {
|
|
if ($this->subChannels !== null) $this->beginTransaction();
|
|
return $this->storage->_delete($this->channel, $filter, $func, $args);
|
|
}
|
|
|
|
function dbAll(array $query, ?array $params=null): iterable {
|
|
$primaryKeys = $this->channel->getPrimaryKeys();
|
|
return $this->storage->db()->all(cl::merge([
|
|
"select",
|
|
"from" => $this->getTableName(),
|
|
], $query), $params, $primaryKeys);
|
|
}
|
|
|
|
function dbOne(array $query, ?array $params=null): ?array {
|
|
return $this->storage->db()->one(cl::merge([
|
|
"select",
|
|
"from" => $this->getTableName(),
|
|
], $query), $params);
|
|
}
|
|
|
|
/** @return int|false */
|
|
function dbUpdate(array $query, ?array $params=null) {
|
|
return $this->storage->db()->exec(cl::merge([
|
|
"update",
|
|
"table" => $this->getTableName(),
|
|
], $query), $params);
|
|
}
|
|
|
|
function close(): void {
|
|
$this->storage->close();
|
|
}
|
|
}
|