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(); } }