diff --git a/src/db/Capacitor.php b/src/db/Capacitor.php index 6a41d5a..b7c7155 100644 --- a/src/db/Capacitor.php +++ b/src/db/Capacitor.php @@ -66,17 +66,22 @@ class Capacitor implements ITransactor { } function beginTransaction(?callable $func=null, bool $commit=true): void { - if ($this->subManageTransactions === null && $this->subChannels !== null) { - foreach ($this->subChannels as $channel) { - $name = $channel->getName(); - $this->subManageTransactions ??= []; - if (!array_key_exists($name, $this->subManageTransactions)) { - $this->subManageTransactions[$name] = $channel->isManageTransactions(); + $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); } - $channel->setManageTransactions(false); + if (!$db->inTransaction()) $db->beginTransaction(); } - $db = $this->db(); - if (!$db->inTransaction()) $db->beginTransaction(); + } elseif (!$db->inTransaction()) { + $db->beginTransaction(); } if ($func !== null) { $commited = false; @@ -105,13 +110,13 @@ class Capacitor implements ITransactor { function commit(): void { $this->beforeEndTransaction(); $db = $this->db(); - if ($db->inTransaction()) $this->db()->commit(); + if ($db->inTransaction()) $db->commit(); } function rollback(): void { $this->beforeEndTransaction(); $db = $this->db(); - if ($db->inTransaction()) $this->db()->rollback(); + if ($db->inTransaction()) $db->rollback(); } function getCreateSql(): string { @@ -131,7 +136,7 @@ class Capacitor implements ITransactor { } function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { - $this->beginTransaction(); + if ($this->subChannels !== null) $this->beginTransaction(); return $this->storage->_charge($this->channel, $item, $func, $args, $values); } @@ -152,12 +157,12 @@ class Capacitor implements ITransactor { } function each($filter, $func=null, ?array $args=null, ?int &$updated=null): int { - $this->beginTransaction(); + if ($this->subChannels !== null) $this->beginTransaction(); return $this->storage->_each($this->channel, $filter, $func, $args, $updated); } function delete($filter, $func=null, ?array $args=null): int { - $this->beginTransaction(); + if ($this->subChannels !== null) $this->beginTransaction(); return $this->storage->_delete($this->channel, $filter, $func, $args); } diff --git a/src/db/CapacitorChannel.php b/src/db/CapacitorChannel.php index 5da0497..bea0b23 100644 --- a/src/db/CapacitorChannel.php +++ b/src/db/CapacitorChannel.php @@ -71,18 +71,6 @@ class CapacitorChannel { return $this->tableName; } - /** - * @var Capacitor|null instance de Capacitor par laquelle cette instance est - * utilisée. ça peut être utile pour implémenter des workflows centrés sur le - * channel - */ - protected ?Capacitor $capacitor; - - function setCapacitor(Capacitor $capacitor): self { - $this->capacitor = $capacitor; - return $this; - } - /** * @var bool indiquer si les modifications de each doivent être gérées dans * une transaction. si false, l'utilisateur doit lui même gérer la @@ -326,4 +314,50 @@ class CapacitorChannel { return true; } const onDelete = "->".[self::class, "onDelete"][1]; + + ############################################################################# + # Méthodes déléguées pour des workflows centrés sur le channel + + /** + * @var Capacitor|null instance de Capacitor par laquelle cette instance est + * utilisée + */ + protected ?Capacitor $capacitor; + + function getCapacitor(): ?Capacitor { + return $this->capacitor; + } + + function setCapacitor(Capacitor $capacitor): self { + $this->capacitor = $capacitor; + return $this; + } + + function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { + return $this->capacitor->charge($item, $func, $args, $values); + } + + function discharge(bool $reset=true): iterable { + return $this->capacitor->discharge($reset); + } + + function count($filter=null): int { + return $this->capacitor->count($filter); + } + + function one($filter): ?array { + return $this->capacitor->one($filter); + } + + function all($filter): iterable { + return $this->capacitor->all($filter); + } + + function each($filter, $func=null, ?array $args=null, ?int &$updated=null): int { + return $this->capacitor->each($filter, $func, $args, $updated); + } + + function delete($filter, $func=null, ?array $args=null): int { + return $this->capacitor->delete($filter, $func, $args); + } } diff --git a/src/db/CapacitorStorage.php b/src/db/CapacitorStorage.php index a204017..4448654 100644 --- a/src/db/CapacitorStorage.php +++ b/src/db/CapacitorStorage.php @@ -47,7 +47,21 @@ abstract class CapacitorStorage { } $definitions[] = $channel->getColumnDefinitions(); $definitions[] = static::COLUMN_DEFINITIONS; - return cl::merge(...$definitions); + # forcer les définitions sans clé à la fin (sqlite requière par exemple que + # primary key (columns) soit à la fin) + $tmp = cl::merge(...$definitions); + $definitions = []; + $constraints = []; + $index = 0; + foreach ($tmp as $col => $def) { + if ($col === $index) { + $index++; + $constraints[] = $def; + } else { + $definitions[$col] = $def; + } + } + return cl::merge($definitions, $constraints); } /** sérialiser les valeurs qui doivent l'être dans $values */ @@ -284,7 +298,8 @@ EOT; # aucune modification if ($insert === null) return 0; - $manageTransactions = $channel->isManageTransactions(); + # si on est déjà dans une transaction, désactiver la gestion des transactions + $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); if ($manageTransactions) { $commited = false; $db->beginTransaction(); @@ -474,15 +489,16 @@ EOT; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); $db = $this->db(); - $tableName = $channel->getTableName(); - $manageTransactions = $channel->isManageTransactions(); - $count = 0; - $updated = 0; + # si on est déjà dans une transaction, désactiver la gestion des transactions + $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); if ($manageTransactions) { $commited = false; $db->beginTransaction(); $commitThreshold = $channel->getEachCommitThreshold(); } + $count = 0; + $updated = 0; + $tableName = $channel->getTableName(); try { $args ??= []; foreach ($this->_all($channel, $filter) as $values) { @@ -541,14 +557,15 @@ EOT; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); $db = $this->db(); - $tableName = $channel->getTableName(); - $manageTransactions = $channel->isManageTransactions(); - $count = 0; + # si on est déjà dans une transaction, désactiver la gestion des transactions + $manageTransactions = $channel->isManageTransactions() && !$db->inTransaction(); if ($manageTransactions) { $commited = false; $db->beginTransaction(); $commitThreshold = $channel->getEachCommitThreshold(); } + $count = 0; + $tableName = $channel->getTableName(); try { $args ??= []; foreach ($this->_all($channel, $filter) as $values) {