changement majeur dans Capacitor
This commit is contained in:
		
							parent
							
								
									9cf7ac908f
								
							
						
					
					
						commit
						8b7b318acd
					
				| @ -138,9 +138,9 @@ class Capacitor implements ITransactor { | ||||
|     $this->storage->_reset($this->channel, $recreate); | ||||
|   } | ||||
| 
 | ||||
|   function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { | ||||
|   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, $values); | ||||
|     return $this->storage->_charge($this->channel, $item, $func, $args, $row); | ||||
|   } | ||||
| 
 | ||||
|   function discharge(bool $reset=true): Traversable { | ||||
|  | ||||
| @ -25,8 +25,6 @@ class CapacitorChannel implements ITransactor { | ||||
| 
 | ||||
|   const EACH_COMMIT_THRESHOLD = 100; | ||||
| 
 | ||||
|   const USE_CACHE = false; | ||||
| 
 | ||||
|   static function verifix_name(?string &$name, ?string &$tableName=null): void { | ||||
|     if ($name !== null) { | ||||
|       $name = strtolower($name); | ||||
| @ -60,7 +58,6 @@ class CapacitorChannel implements ITransactor { | ||||
|     $this->tableName = $tableName; | ||||
|     $this->manageTransactions = $manageTransactions ?? static::MANAGE_TRANSACTIONS; | ||||
|     $this->eachCommitThreshold = self::verifix_eachCommitThreshold($eachCommitThreshold); | ||||
|     $this->useCache = static::USE_CACHE; | ||||
|     $this->setup = false; | ||||
|     $this->created = false; | ||||
|     $columnDefinitions = $this->COLUMN_DEFINITIONS(); | ||||
| @ -168,23 +165,6 @@ class CapacitorChannel implements ITransactor { | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @var bool faut-il passer par le cache pour les requêtes de all(), each() | ||||
|    * et delete()? | ||||
|    * ça peut être nécessaire avec MySQL/MariaDB si on utilise les requêtes non | ||||
|    * bufférisées, et que la fonction manipule la base de données | ||||
|    */ | ||||
|   protected bool $useCache; | ||||
| 
 | ||||
|   function isUseCache(): bool { | ||||
|     return $this->useCache; | ||||
|   } | ||||
| 
 | ||||
|   function setUseCache(bool $useCache=true): self { | ||||
|     $this->useCache = $useCache; | ||||
|     return $this; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * initialiser ce channel avant sa première utilisation. | ||||
|    */ | ||||
| @ -283,8 +263,8 @@ class CapacitorChannel implements ITransactor { | ||||
|    * | ||||
|    * cette méthode doit être utilisée dans {@link self::onUpdate()} | ||||
|    */ | ||||
|   function wasRowModified(array $values, array $pvalues): bool { | ||||
|     return $values["item__sum_"] !== $pvalues["item__sum_"]; | ||||
|   function wasRowModified(array $row, array $prow): bool { | ||||
|     return $row["item__sum_"] !== $prow["item__sum_"]; | ||||
|   } | ||||
| 
 | ||||
|   final function serialize($item): ?string { | ||||
| @ -315,17 +295,17 @@ class CapacitorChannel implements ITransactor { | ||||
|     return array_combine($sumCols, [$serial, $sum]); | ||||
|   } | ||||
| 
 | ||||
|   function wasSumModified(string $key, $value, array $pvalues): bool { | ||||
|   function wasSumModified(string $key, $value, array $prow): bool { | ||||
|     $sumCol = $this->getSumCols($key)[1]; | ||||
|     $sum = $this->sum(null, $value); | ||||
|     $psum = $pvalues[$sumCol] ?? $this->sum(null, $pvalues[$key] ?? null); | ||||
|     $psum = $prow[$sumCol] ?? $this->sum(null, $prow[$key] ?? null); | ||||
|     return $sum !== $psum; | ||||
|   } | ||||
| 
 | ||||
|   function _wasSumModified(string $key, array $row, array $prow): bool { | ||||
|   function _wasSumModified(string $key, array $raw, array $praw): bool { | ||||
|     $sumCol = $this->getSumCols($key)[1]; | ||||
|     $sum = $row[$sumCol] ?? null; | ||||
|     $psum = $prow[$sumCol] ?? null; | ||||
|     $sum = $raw[$sumCol] ?? null; | ||||
|     $psum = $praw[$sumCol] ?? null; | ||||
|     return $sum !== $psum; | ||||
|   } | ||||
| 
 | ||||
| @ -334,9 +314,9 @@ class CapacitorChannel implements ITransactor { | ||||
|    * créer un nouvel élément | ||||
|    * | ||||
|    * @param mixed $item l'élément à charger | ||||
|    * @param array $values la ligne à créer, calculée à partir de $item et des | ||||
|    * @param array $row la ligne à créer, calculée à partir de $item et des | ||||
|    * valeurs retournées par {@link getItemValues()} | ||||
|    * @return ?array le cas échéant, un tableau non null à merger dans $values et | ||||
|    * @return ?array le cas échéant, un tableau non null à merger dans $row et | ||||
|    * utilisé pour provisionner la ligne nouvellement créée. | ||||
|    * Retourner [false] pour annuler le chargement (la ligne n'est pas créée) | ||||
|    * | ||||
| @ -348,7 +328,7 @@ class CapacitorChannel implements ITransactor { | ||||
|    * peut techniquement retourner de nouvelles valeurs pour la clé primaire, ça | ||||
|    * risque de créer des doublons | ||||
|    */ | ||||
|   function onCreate($item, array $values, ?array $alwaysNull): ?array { | ||||
|   function onCreate($item, array $row, ?array $alwaysNull): ?array { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
| @ -357,12 +337,12 @@ class CapacitorChannel implements ITransactor { | ||||
|    * mettre à jour un élément existant | ||||
|    * | ||||
|    * @param mixed $item l'élément à charger | ||||
|    * @param array $values la nouvelle ligne, calculée à partir de $item et | ||||
|    * @param array $row la nouvelle ligne, calculée à partir de $item et | ||||
|    * des valeurs retournées par {@link getItemValues()} | ||||
|    * @param array $pvalues la précédente ligne, chargée depuis la base de | ||||
|    * @param array $prow la précédente ligne, chargée depuis la base de | ||||
|    * données | ||||
|    * @return ?array null s'il ne faut pas mettre à jour la ligne. sinon, ce | ||||
|    * tableau est mergé dans $values puis utilisé pour mettre à jour la ligne | ||||
|    * tableau est mergé dans $row puis utilisé pour mettre à jour la ligne | ||||
|    * existante | ||||
|    * Retourner [false] pour annuler le chargement (la ligne n'est pas mise à | ||||
|    * jour) | ||||
| @ -371,7 +351,7 @@ class CapacitorChannel implements ITransactor { | ||||
|    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être | ||||
|    * modifiée. si elle est retournée, elle est ignorée | ||||
|    */ | ||||
|   function onUpdate($item, array $values, array $pvalues): ?array { | ||||
|   function onUpdate($item, array $row, array $prow): ?array { | ||||
|     return null; | ||||
|   } | ||||
| 
 | ||||
| @ -379,8 +359,8 @@ class CapacitorChannel implements ITransactor { | ||||
|    * méthode appelée lors du parcours des éléments avec | ||||
|    * {@link Capacitor::each()} | ||||
|    * | ||||
|    * @param mixed $item l'élément courant | ||||
|    * @param ?array $values la ligne courante | ||||
|    * @param ?array $row la ligne courante. l'élément courant est accessible via | ||||
|    * $row["item"] | ||||
|    * @return ?array le cas échéant, un tableau non null utilisé pour mettre à | ||||
|    * jour la ligne courante | ||||
|    * | ||||
| @ -388,7 +368,7 @@ class CapacitorChannel implements ITransactor { | ||||
|    * - La clé primaire (il s'agit généralement de "id_") ne peut pas être | ||||
|    * modifiée. si elle est retournée, elle est ignorée | ||||
|    */ | ||||
|   function onEach($item, array $values): ?array { | ||||
|   function onEach(array $row): ?array { | ||||
|     return null; | ||||
|   } | ||||
|   const onEach = "->".[self::class, "onEach"][1]; | ||||
| @ -397,11 +377,11 @@ class CapacitorChannel implements ITransactor { | ||||
|    * méthode appelée lors du parcours des éléments avec | ||||
|    * {@link Capacitor::delete()} | ||||
|    * | ||||
|    * @param mixed $item l'élément courant | ||||
|    * @param ?array $values la ligne courante | ||||
|    * @param ?array $row la ligne courante. l'élément courant est accessible via | ||||
|    * $row["item"] | ||||
|    * @return bool true s'il faut supprimer la ligne, false sinon | ||||
|    */ | ||||
|   function onDelete($item, array $values): bool { | ||||
|   function onDelete(array $row): bool { | ||||
|     return true; | ||||
|   } | ||||
|   const onDelete = "->".[self::class, "onDelete"][1]; | ||||
| @ -460,8 +440,8 @@ class CapacitorChannel implements ITransactor { | ||||
|     $this->capacitor->reset($recreate); | ||||
|   } | ||||
| 
 | ||||
|   function charge($item, $func=null, ?array $args=null, ?array &$values=null): int { | ||||
|     return $this->capacitor->charge($item, $func, $args, $values); | ||||
|   function charge($item, $func=null, ?array $args=null, ?array &$row=null): int { | ||||
|     return $this->capacitor->charge($item, $func, $args, $row); | ||||
|   } | ||||
| 
 | ||||
|   function discharge(bool $reset=true): Traversable { | ||||
|  | ||||
| @ -125,9 +125,35 @@ abstract class CapacitorStorage { | ||||
|     return $channel->getMigration(); | ||||
|   } | ||||
| 
 | ||||
|   /** sérialiser les valeurs qui doivent l'être dans $values */ | ||||
|   protected function serialize(CapacitorChannel $channel, ?array $values): ?array { | ||||
|     if ($values === null) return null; | ||||
|   /** sérialiser les valeurs qui doivent l'être dans $row */ | ||||
|   protected function serialize(CapacitorChannel $channel, ?array $row): ?array { | ||||
|     if ($row === null) return null; | ||||
|     $cols = $this->ColumnDefinitions($channel); | ||||
|     $index = 0; | ||||
|     $raw = []; | ||||
|     foreach (array_keys($cols) as $col) { | ||||
|       $key = $col; | ||||
|       if ($key === $index) { | ||||
|         $index++; | ||||
|       } elseif ($channel->isSerialCol($key)) { | ||||
|         [$serialCol, $sumCol] = $channel->getSumCols($key); | ||||
|         if (array_key_exists($key, $row)) { | ||||
|           $sum = $channel->getSum($key, $row[$key]); | ||||
|           $raw[$serialCol] = $sum[$serialCol]; | ||||
|           if (array_key_exists($sumCol, $cols)) { | ||||
|             $raw[$sumCol] = $sum[$sumCol]; | ||||
|           } | ||||
|         } | ||||
|       } elseif (array_key_exists($key, $row)) { | ||||
|         $raw[$col] = $row[$key]; | ||||
|       } | ||||
|     } | ||||
|     return $raw; | ||||
|   } | ||||
| 
 | ||||
|   /** désérialiser les valeurs qui doivent l'être dans $values */ | ||||
|   protected function unserialize(CapacitorChannel $channel, ?array $raw): ?array { | ||||
|     if ($raw === null) return null; | ||||
|     $cols = $this->ColumnDefinitions($channel); | ||||
|     $index = 0; | ||||
|     $row = []; | ||||
| @ -135,44 +161,18 @@ abstract class CapacitorStorage { | ||||
|       $key = $col; | ||||
|       if ($key === $index) { | ||||
|         $index++; | ||||
|       } elseif (!array_key_exists($col, $raw)) { | ||||
|       } elseif ($channel->isSerialCol($key)) { | ||||
|         [$serialCol, $sumCol] = $channel->getSumCols($key); | ||||
|         if (array_key_exists($key, $values)) { | ||||
|           $sum = $channel->getSum($key, $values[$key]); | ||||
|           $row[$serialCol] = $sum[$serialCol]; | ||||
|           if (array_key_exists($sumCol, $cols)) { | ||||
|             $row[$sumCol] = $sum[$sumCol]; | ||||
|           } | ||||
|         } | ||||
|       } elseif (array_key_exists($key, $values)) { | ||||
|         $row[$col] = $values[$key]; | ||||
|         $value = $raw[$col]; | ||||
|         if ($value !== null) $value = $channel->unserialize($value); | ||||
|         $row[$key] = $value; | ||||
|       } else { | ||||
|         $row[$key] = $raw[$col]; | ||||
|       } | ||||
|     } | ||||
|     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++; | ||||
|       } elseif (!array_key_exists($col, $row)) { | ||||
|       } elseif ($channel->isSerialCol($key)) { | ||||
|         $value = $row[$col]; | ||||
|         if ($value !== null) $value = $channel->unserialize($value); | ||||
|         $values[$key] = $value; | ||||
|       } else { | ||||
|         $values[$key] = $row[$col]; | ||||
|       } | ||||
|     } | ||||
|     return $values; | ||||
|   } | ||||
| 
 | ||||
|   function getPrimaryKeys(CapacitorChannel $channel): array { | ||||
|     $primaryKeys = $channel->getPrimaryKeys(); | ||||
|     if ($primaryKeys === null) $primaryKeys = ["id_"]; | ||||
| @ -238,13 +238,13 @@ abstract class CapacitorStorage { | ||||
|     "class_name" => "varchar", | ||||
|   ]; | ||||
| 
 | ||||
|   function channelExists(string $name, ?array &$row=null): bool { | ||||
|     $row = $this->db()->one([ | ||||
|   function channelExists(string $name, ?array &$raw=null): bool { | ||||
|     $raw = $this->db()->one([ | ||||
|       "select", | ||||
|       "from" => static::CHANNELS_TABLE, | ||||
|       "where" => ["name" => $name], | ||||
|     ]); | ||||
|     return $row !== null; | ||||
|     return $raw !== null; | ||||
|   } | ||||
| 
 | ||||
|   function getChannels(): iterable { | ||||
| @ -355,7 +355,7 @@ abstract class CapacitorStorage { | ||||
|    * en fonction du type d'opération: création ou mise à jour | ||||
|    * | ||||
|    * Dans les deux cas, si la fonction retourne un tableau, il est utilisé pour | ||||
|    * modifier les valeurs insérées/mises à jour. De plus, $values obtient la | ||||
|    * modifier les valeurs insérées/mises à jour. De plus, $row obtient la | ||||
|    * valeur finale des données insérées/mises à jour | ||||
|    * | ||||
|    * Si $args est renseigné, il est ajouté aux arguments utilisés pour appeler | ||||
| @ -366,27 +366,27 @@ abstract class CapacitorStorage { | ||||
|    * @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(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$values=null): int { | ||||
|   function _charge(CapacitorChannel $channel, $item, $func, ?array $args, ?array &$row=null): int { | ||||
|     $this->_create($channel); | ||||
|     $tableName = $channel->getTableName(); | ||||
|     $db = $this->db(); | ||||
|     $args ??= []; | ||||
| 
 | ||||
|     $values = func::call([$channel, "getItemValues"], $item, ...$args); | ||||
|     if ($values === [false]) return 0; | ||||
|     $row = func::call([$channel, "getItemValues"], $item, ...$args); | ||||
|     if ($row === [false]) return 0; | ||||
| 
 | ||||
|     if (array_key_exists("item", $values)) { | ||||
|       $item = A::pop($values, "item"); | ||||
|     if ($row !== null && array_key_exists("item", $row)) { | ||||
|       $item = A::pop($row, "item"); | ||||
|     } | ||||
| 
 | ||||
|     $row = cl::merge( | ||||
|     $raw = cl::merge( | ||||
|       $channel->getSum("item", $item), | ||||
|       $this->serialize($channel, $values)); | ||||
|     $prow = null; | ||||
|     $rowIds = $this->getRowIds($channel, $row, $primaryKeys); | ||||
|       $this->serialize($channel, $row)); | ||||
|     $praw = null; | ||||
|     $rowIds = $this->getRowIds($channel, $raw, $primaryKeys); | ||||
|     if ($rowIds !== null) { | ||||
|       # modification
 | ||||
|       $prow = $db->one([ | ||||
|       $praw = $db->one([ | ||||
|         "select", | ||||
|         "from" => $tableName, | ||||
|         "where" => $rowIds, | ||||
| @ -395,47 +395,47 @@ abstract class CapacitorStorage { | ||||
| 
 | ||||
|     $now = date("Y-m-d H:i:s"); | ||||
|     $insert = null; | ||||
|     if ($prow === null) { | ||||
|     if ($praw === null) { | ||||
|       # création
 | ||||
|       $row = cl::merge($row, [ | ||||
|       $raw = cl::merge($raw, [ | ||||
|         "created_" => $now, | ||||
|         "modified_" => $now, | ||||
|       ]); | ||||
|       $insert = true; | ||||
|       $initFunc = func::with([$channel, "onCreate"], $args); | ||||
|       $values = $this->unserialize($channel, $row); | ||||
|       $pvalues = null; | ||||
|       $row = $this->unserialize($channel, $raw); | ||||
|       $prow = null; | ||||
|     } else { | ||||
|       # modification
 | ||||
|       # intégrer autant que possible les valeurs de prow dans row, de façon que
 | ||||
|       # intégrer autant que possible les valeurs de praw dans raw, de façon que
 | ||||
|       # l'utilisateur puisse voir clairement ce qui a été modifié
 | ||||
|       if ($channel->_wasSumModified("item", $row, $prow)) { | ||||
|       if ($channel->_wasSumModified("item", $raw, $praw)) { | ||||
|         $insert = false; | ||||
|         $row = cl::merge($prow, $row, [ | ||||
|         $raw = cl::merge($praw, $raw, [ | ||||
|           "modified_" => $now, | ||||
|         ]); | ||||
|       } else { | ||||
|         $row = cl::merge($prow, $row); | ||||
|         $raw = cl::merge($praw, $raw); | ||||
|       } | ||||
|       $initFunc = func::with([$channel, "onUpdate"], $args); | ||||
|       $values = $this->unserialize($channel, $row); | ||||
|       $pvalues = $this->unserialize($channel, $prow); | ||||
|       $row = $this->unserialize($channel, $raw); | ||||
|       $prow = $this->unserialize($channel, $praw); | ||||
|     } | ||||
| 
 | ||||
|     $updates = $initFunc->prependArgs([$item, $values, $pvalues])->invoke(); | ||||
|     $updates = $initFunc->prependArgs([$item, $row, $prow])->invoke(); | ||||
|     if ($updates === [false]) return 0; | ||||
|     if (is_array($updates) && $updates) { | ||||
|       if ($insert === null) $insert = false; | ||||
|       if (!array_key_exists("modified_", $updates)) { | ||||
|         $updates["modified_"] = $now; | ||||
|       } | ||||
|       $values = cl::merge($values, $updates); | ||||
|       $row = cl::merge($row, $this->serialize($channel, $updates)); | ||||
|       $row = cl::merge($row, $updates); | ||||
|       $raw = cl::merge($raw, $this->serialize($channel, $updates)); | ||||
|     } | ||||
| 
 | ||||
|     if ($func !== null) { | ||||
|       $updates = func::with($func) | ||||
|         ->prependArgs([$item, $values, $pvalues]) | ||||
|         ->prependArgs([$item, $row, $prow]) | ||||
|         ->bind($channel) | ||||
|         ->invoke(); | ||||
|       if ($updates === [false]) return 0; | ||||
| @ -444,8 +444,8 @@ abstract class CapacitorStorage { | ||||
|         if (!array_key_exists("modified_", $updates)) { | ||||
|           $updates["modified_"] = $now; | ||||
|         } | ||||
|         $values = cl::merge($values, $updates); | ||||
|         $row = cl::merge($row, $this->serialize($channel, $updates)); | ||||
|         $row = cl::merge($row, $updates); | ||||
|         $raw = cl::merge($raw, $this->serialize($channel, $updates)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
| @ -464,25 +464,23 @@ abstract class CapacitorStorage { | ||||
|         $id = $db->exec([ | ||||
|           "insert", | ||||
|           "into" => $tableName, | ||||
|           "values" => $row, | ||||
|           "values" => $raw, | ||||
|         ]); | ||||
|         if (count($primaryKeys) == 1 && $rowIds === null) { | ||||
|           # mettre à jour avec l'id généré
 | ||||
|           $values[$primaryKeys[0]] = $id; | ||||
|           $row[$primaryKeys[0]] = $id; | ||||
|         } | ||||
|         $nbModified = 1; | ||||
|       } else { | ||||
|         # calculer ce qui a changé pour ne mettre à jour que le nécessaire
 | ||||
|         $updates = []; | ||||
|         foreach ($row as $col => $value) { | ||||
|         foreach ($raw as $col => $value) { | ||||
|           if (array_key_exists($col, $rowIds)) { | ||||
|             # ne jamais mettre à jour la clé primaire
 | ||||
|             continue; | ||||
|           } | ||||
|           $pvalue = $prow[$col] ?? null; | ||||
|           if ($value !== ($pvalue)) { | ||||
|             $updates[$col] = $value; | ||||
|           } | ||||
|           $pvalue = $praw[$col] ?? null; | ||||
|           if ($value !== $pvalue) $updates[$col] = $value; | ||||
|         } | ||||
|         if (count($updates) == 1 && array_key_first($updates) == "modified_") { | ||||
|           # si l'unique modification porte sur la date de modification, alors
 | ||||
| @ -510,19 +508,22 @@ abstract class CapacitorStorage { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$values=null): int { | ||||
|     return $this->_charge($this->getChannel($channel), $item, $func, $args, $values); | ||||
|   function charge(?string $channel, $item, $func=null, ?array $args=null, ?array &$row=null): int { | ||||
|     return $this->_charge($this->getChannel($channel), $item, $func, $args, $row); | ||||
|   } | ||||
| 
 | ||||
|   /** décharger les données du canal spécifié */ | ||||
|   /** | ||||
|    * décharger les données du canal spécifié. seul la valeur de $item est | ||||
|    * fournie | ||||
|    */ | ||||
|   function _discharge(CapacitorChannel $channel, bool $reset=true): Traversable { | ||||
|     $this->_create($channel); | ||||
|     $rows = $this->db()->all([ | ||||
|     $raws = $this->db()->all([ | ||||
|       "select item__", | ||||
|       "from" => $channel->getTableName(), | ||||
|     ]); | ||||
|     foreach ($rows as $row) { | ||||
|       yield unserialize($row['item__']); | ||||
|     foreach ($raws as $raw) { | ||||
|       yield unserialize($raw['item__']); | ||||
|     } | ||||
|     if ($reset) $this->_reset($channel); | ||||
|   } | ||||
| @ -591,45 +592,34 @@ abstract class CapacitorStorage { | ||||
|     if ($filter === null) throw ValueException::null("filter"); | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     $row = $this->db()->one(cl::merge([ | ||||
|     $raw = $this->db()->one(cl::merge([ | ||||
|       "select", | ||||
|       "from" => $channel->getTableName(), | ||||
|       "where" => $filter, | ||||
|     ], $mergeQuery)); | ||||
|     return $this->unserialize($channel, $row); | ||||
|     return $this->unserialize($channel, $raw); | ||||
|   } | ||||
| 
 | ||||
|   function one(?string $channel, $filter, ?array $mergeQuery=null): ?array { | ||||
|     return $this->_one($this->getChannel($channel), $filter, $mergeQuery); | ||||
|   } | ||||
| 
 | ||||
|   private function _allCached(string $id, CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable { | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     $rows = $this->db()->all(cl::merge([ | ||||
|       "select", | ||||
|       "from" => $channel->getTableName(), | ||||
|       "where" => $filter, | ||||
|     ], $mergeQuery), null, $this->getPrimaryKeys($channel)); | ||||
|     if ($channel->isUseCache()) { | ||||
|       $cacheIds = [$id, get_class($channel)]; | ||||
|       cache::get()->resetCached($cacheIds); | ||||
|       $rows = cache::new(null, $cacheIds, function() use ($rows) { | ||||
|         yield from $rows; | ||||
|       }); | ||||
|     } | ||||
|     foreach ($rows as $key => $row) { | ||||
|       yield $key => $this->unserialize($channel, $row); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * 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(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable { | ||||
|     return $this->_allCached("all", $channel, $filter, $mergeQuery); | ||||
|     $this->_create($channel); | ||||
|     $this->verifixFilter($channel, $filter); | ||||
|     $raws = $this->db()->all(cl::merge([ | ||||
|       "select", | ||||
|       "from" => $channel->getTableName(), | ||||
|       "where" => $filter, | ||||
|     ], $mergeQuery), null, $this->getPrimaryKeys($channel)); | ||||
|     foreach ($raws as $key => $raw) { | ||||
|       yield $key => $this->unserialize($channel, $raw); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   function all(?string $channel, $filter, $mergeQuery=null): Traversable { | ||||
| @ -665,10 +655,10 @@ abstract class CapacitorStorage { | ||||
|     $tableName = $channel->getTableName(); | ||||
|     try { | ||||
|       $args ??= []; | ||||
|       $all = $this->_allCached("each", $channel, $filter, $mergeQuery); | ||||
|       foreach ($all as $values) { | ||||
|         $rowIds = $this->getRowIds($channel, $values); | ||||
|         $updates = $onEach->invoke([$values["item"], $values, ...$args]); | ||||
|       $rows = $this->_all($channel, $filter, $mergeQuery); | ||||
|       foreach ($rows as $row) { | ||||
|         $rowIds = $this->getRowIds($channel, $row); | ||||
|         $updates = $onEach->invoke([$row, ...$args]); | ||||
|         if (is_array($updates) && $updates) { | ||||
|           if (!array_key_exists("modified_", $updates)) { | ||||
|             $updates["modified_"] = date("Y-m-d H:i:s"); | ||||
| @ -732,10 +722,10 @@ abstract class CapacitorStorage { | ||||
|     $tableName = $channel->getTableName(); | ||||
|     try { | ||||
|       $args ??= []; | ||||
|       $all = $this->_allCached("delete", $channel, $filter); | ||||
|       foreach ($all as $values) { | ||||
|         $rowIds = $this->getRowIds($channel, $values); | ||||
|         $shouldDelete = boolval($onDelete->invoke([$values["item"], $values, ...$args])); | ||||
|       $rows = $this->_all($channel, $filter); | ||||
|       foreach ($rows as $row) { | ||||
|         $rowIds = $this->getRowIds($channel, $row); | ||||
|         $shouldDelete = boolval($onDelete->invoke([$row, ...$args])); | ||||
|         if ($shouldDelete) { | ||||
|           $db->exec([ | ||||
|             "delete", | ||||
|  | ||||
| @ -73,7 +73,8 @@ class SqliteStorageTest extends TestCase { | ||||
|     $capacitor->charge(["name" => "third", "age" => 15]); | ||||
|     $capacitor->charge(["name" => "fourth", "age" => 20]); | ||||
| 
 | ||||
|     $setDone = function ($item, $row, $suffix=null) { | ||||
|     $setDone = function ($row, $suffix=null) { | ||||
|       $item = $row["item"]; | ||||
|       $updates = ["done" => 1]; | ||||
|       if ($suffix !== null) { | ||||
|         $item["name"] .= $suffix; | ||||
| @ -82,7 +83,7 @@ class SqliteStorageTest extends TestCase { | ||||
|       return $updates; | ||||
|     }; | ||||
|     $capacitor->each(["age" => [">", 10]], $setDone, ["++"]); | ||||
|     $capacitor->each(["done" => 0], $setDone, null); | ||||
|     $capacitor->each(["done" => 0], $setDone); | ||||
| 
 | ||||
|     self::Txx(cl::all($capacitor->discharge(false))); | ||||
|     $capacitor->close(); | ||||
| @ -147,8 +148,8 @@ class SqliteStorageTest extends TestCase { | ||||
|       "from" => $capacitor->getChannel()->getTableName(), | ||||
|     ]))); | ||||
|     self::Txx("=== each"); | ||||
|     $capacitor->each(null, function ($item, $values) { | ||||
|       self::Txx($values); | ||||
|     $capacitor->each(null, function ($row) { | ||||
|       self::Txx($row); | ||||
|     }); | ||||
| 
 | ||||
|     $capacitor->close(); | ||||
| @ -176,99 +177,100 @@ class SqliteStorageTest extends TestCase { | ||||
|     }); | ||||
| 
 | ||||
|     $capacitor->reset(); | ||||
|     $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) { | ||||
|     $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame("first", $item["name"]); | ||||
|       self::assertSame(5, $item["age"]); | ||||
|       self::assertnotnull($values); | ||||
|       self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($values)); | ||||
|       self::assertnotnull($row); | ||||
|       self::assertSame(["name", "age", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 5, | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "item"])); | ||||
|       self::assertNull($pvalues); | ||||
|       ], cl::select($row, ["name", "age", "item"])); | ||||
|       self::assertNull($prow); | ||||
|     }); | ||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { | ||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame("first", $item["name"]); | ||||
|       self::assertSame(10, $item["age"]); | ||||
|       self::assertnotnull($values); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); | ||||
|       self::assertnotnull($row); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 10, | ||||
|         "done" => 0, | ||||
|         "notes" => null, | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($pvalues); | ||||
|       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($prow); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 5, | ||||
|         "done" => 0, | ||||
|         "notes" => null, | ||||
|         "item" => ["name" => "first", "age" => 5], | ||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); | ||||
|       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||
|     }); | ||||
| 
 | ||||
|     $capacitor->each(null, function($item, ?array $values) { | ||||
|     $capacitor->each(null, function(array $row) { | ||||
|       $item = $row["item"]; | ||||
|       self::assertSame("first", $item["name"]); | ||||
|       self::assertSame(10, $item["age"]); | ||||
|       self::assertnotnull($values); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); | ||||
|       self::assertnotnull($row); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 10, | ||||
|         "done" => 0, | ||||
|         "notes" => null, | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); | ||||
|       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||
|       return [ | ||||
|         "done" => 1, | ||||
|         "notes" => "modified", | ||||
|       ]; | ||||
|     }); | ||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { | ||||
|     $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame("first", $item["name"]); | ||||
|       self::assertSame(10, $item["age"]); | ||||
|       self::assertnotnull($values); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); | ||||
|       self::assertnotnull($row); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 10, | ||||
|         "done" => 1, | ||||
|         "notes" => "modified", | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($pvalues); | ||||
|       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($prow); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 10, | ||||
|         "done" => 1, | ||||
|         "notes" => "modified", | ||||
|         "item" => $item, | ||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); | ||||
|       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||
|     }); | ||||
| 
 | ||||
|     $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) { | ||||
|     $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame("first", $item["name"]); | ||||
|       self::assertSame(20, $item["age"]); | ||||
|       self::assertnotnull($values); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($values)); | ||||
|       self::assertnotnull($row); | ||||
|       self::assertSame(["name", "age", "done", "notes", "item", "item__sum_", "created_", "modified_"], array_keys($row)); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 20, | ||||
|         "done" => 1, | ||||
|         "notes" => "modified", | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($pvalues); | ||||
|       ], cl::select($row, ["name", "age", "done", "notes", "item"])); | ||||
|       self::assertNotNull($prow); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", | ||||
|         "age" => 10, | ||||
|         "done" => 1, | ||||
|         "notes" => "modified", | ||||
|         "item" => ["name" => "first", "age" => 10], | ||||
|       ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); | ||||
|       ], cl::select($prow, ["name", "age", "done", "notes", "item"])); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
| @ -293,55 +295,55 @@ class SqliteStorageTest extends TestCase { | ||||
|     }); | ||||
| 
 | ||||
|     $capacitor->reset(); | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) { | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 5, | ||||
|         "item" => $item, | ||||
|       ], cl::select($values, ["name", "age", "item"])); | ||||
|       ], cl::select($row, ["name", "age", "item"])); | ||||
|       return ["item" => null]; | ||||
|     }); | ||||
|     self::assertSame(1, $nbModified); | ||||
|     sleep(1); | ||||
|     # nb: on met des sleep() pour que la date de modification soit systématiquement différente
 | ||||
| 
 | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) { | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 10, | ||||
|         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", | ||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 5, | ||||
|         "item" => null, "item__sum_" => null, | ||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||
|       return ["item" => null]; | ||||
|     }); | ||||
|     self::assertSame(1, $nbModified); | ||||
|     sleep(1); | ||||
| 
 | ||||
|     # pas de modification ici
 | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $values, ?array $pvalues) { | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 10, | ||||
|         "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", | ||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 10, | ||||
|         "item" => null, "item__sum_" => null, | ||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||
|       return ["item" => null]; | ||||
|     }); | ||||
|     self::assertSame(0, $nbModified); | ||||
|     sleep(1); | ||||
| 
 | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $values, ?array $pvalues) { | ||||
|     $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $row, ?array $prow) { | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 20, | ||||
|         "item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5", | ||||
|       ], cl::select($values, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($row, ["name", "age", "item", "item__sum_"])); | ||||
|       self::assertSame([ | ||||
|         "name" => "first", "age" => 10, | ||||
|         "item" => null, "item__sum_" => null, | ||||
|       ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); | ||||
|       ], cl::select($prow, ["name", "age", "item", "item__sum_"])); | ||||
|       return ["item" => null]; | ||||
|     }); | ||||
|     self::assertSame(1, $nbModified); | ||||
|  | ||||
| @ -3,6 +3,7 @@ namespace nulib\db\sqlite; | ||||
| 
 | ||||
| use Exception; | ||||
| use nulib\tests\TestCase; | ||||
| use nulib\ValueException; | ||||
| 
 | ||||
| class SqliteTest extends TestCase { | ||||
|   const CREATE_PERSON = "create table person(nom varchar, prenom varchar, age integer)"; | ||||
| @ -12,8 +13,8 @@ class SqliteTest extends TestCase { | ||||
|   function testMigration() { | ||||
|     $sqlite = new Sqlite(":memory:", [ | ||||
|       "migration" => [ | ||||
|         self::CREATE_PERSON, | ||||
|         self::INSERT_JEPHTE, | ||||
|         "create" => self::CREATE_PERSON, | ||||
|         "insert" => self::INSERT_JEPHTE, | ||||
|       ], | ||||
|     ]); | ||||
|     self::assertSame("clain", $sqlite->get("select nom, age from person")); | ||||
| @ -30,15 +31,15 @@ class SqliteTest extends TestCase { | ||||
|     ], $sqlite->get("select nom, age from person where nom = 'payet'", null, true)); | ||||
| 
 | ||||
|     self::assertSame([ | ||||
|       ["key" => "0", "value" => self::CREATE_PERSON, "done" => 1], | ||||
|       ["key" => "1", "value" => self::INSERT_JEPHTE, "done" => 1], | ||||
|     ], iterator_to_array($sqlite->all("select key, value, done from _migration"))); | ||||
|       ["channel" => "", "name" => "create", "done" => 1], | ||||
|       ["channel" => "", "name" => "insert", "done" => 1], | ||||
|     ], iterator_to_array($sqlite->all("select channel, name, done from _migration"))); | ||||
|   } | ||||
| 
 | ||||
|   function testException() { | ||||
|     $sqlite = new Sqlite(":memory:"); | ||||
|     self::assertException(Exception::class, [$sqlite, "exec"], "prout"); | ||||
|     self::assertException(SqliteException::class, [$sqlite, "exec"], ["prout"]); | ||||
|     self::assertException(ValueException::class, [$sqlite, "exec"], ["prout"]); | ||||
|   } | ||||
| 
 | ||||
|   protected function assertInserted(Sqlite $sqlite, array $row, array $query): void { | ||||
| @ -141,6 +142,10 @@ class SqliteTest extends TestCase { | ||||
| 
 | ||||
|     self::assertSame([ | ||||
|       ["count" => 2], | ||||
|     ], iterator_to_array($sqlite->all(["select count(name) as count from user", "group by" => ["amount"], "having" => ["count(name) = 2"]]))); | ||||
|     ], iterator_to_array($sqlite->all([ | ||||
|       "select count(name) as count from user", | ||||
|       "group by" => ["amount"], | ||||
|       "having" => ["count(name) = 2"], | ||||
|     ]))); | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user