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