From 9eec059cc1f3b89f3c91eb96f4de72d82af096ff Mon Sep 17 00:00:00 2001 From: Jephte Clain Date: Sat, 29 Jun 2024 10:32:57 +0400 Subject: [PATCH] modifs.mineures sans commentaires --- src/db/CapacitorChannel.php | 2 + src/db/CapacitorStorage.php | 47 +++++-- tests/db/sqlite/.gitignore | 2 +- tests/db/sqlite/SqliteStorageTest.php | 193 ++++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 11 deletions(-) diff --git a/src/db/CapacitorChannel.php b/src/db/CapacitorChannel.php index 647be6b..891d7de 100644 --- a/src/db/CapacitorChannel.php +++ b/src/db/CapacitorChannel.php @@ -309,6 +309,7 @@ class CapacitorChannel { function onEach($item, array $values): ?array { return null; } + const onEach = "->".[self::class, "onEach"][1]; /** * méthode appelée lors du parcours des éléments avec @@ -321,4 +322,5 @@ class CapacitorChannel { function onDelete($item, array $values): bool { return true; } + const onDelete = "->".[self::class, "onDelete"][1]; } diff --git a/src/db/CapacitorStorage.php b/src/db/CapacitorStorage.php index 2d28308..a204017 100644 --- a/src/db/CapacitorStorage.php +++ b/src/db/CapacitorStorage.php @@ -241,11 +241,15 @@ EOT; $pvalues = null; } else { # modification + # intégrer autant que possible les valeurs de prow dans row, de façon que + # l'utilisateur puisse voir clairement ce qui a été modifié if ($channel->_wasSumModified("item", $row, $prow)) { $insert = false; - $row = cl::merge($row, [ + $row = cl::merge($prow, $row, [ "modified_" => $now, ]); + } else { + $row = cl::merge($prow, $row); } $initFunc = [$channel, "onUpdate"]; $initArgs = $args; @@ -285,6 +289,7 @@ EOT; $commited = false; $db->beginTransaction(); } + $nbModified = 0; try { if ($insert) { $id = $db->exec([ @@ -296,19 +301,41 @@ EOT; # mettre à jour avec l'id généré $values[$primaryKeys[0]] = $id; } + $nbModified = 1; } else { - $db->exec([ - "update", - "table" => $tableName, - "values" => $row, - "where" => $rowIds, - ]); + # calculer ce qui a changé pour ne mettre à jour que le nécessaire + $updates = []; + foreach ($row 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; + } + } + if (count($updates) == 1 && array_key_first($updates) == "modified_") { + # si l'unique modification porte sur la date de modification, alors + # la ligne n'est pas modifiée. ce cas se présente quand on altère la + # valeur de $item + $updates = null; + } + if ($updates) { + $db->exec([ + "update", + "table" => $tableName, + "values" => $updates, + "where" => $rowIds, + ]); + $nbModified = 1; + } } if ($manageTransactions) { $db->commit(); $commited = true; } - return 1; + return $nbModified; } finally { if ($manageTransactions && !$commited) $db->rollback(); } @@ -443,7 +470,7 @@ EOT; */ function _each(CapacitorChannel $channel, $filter, $func, ?array $args, ?int &$updated=null): int { $this->_create($channel); - if ($func === null) $func = "->onEach"; + if ($func === null) $func = CapacitorChannel::onEach; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); $db = $this->db(); @@ -510,7 +537,7 @@ EOT; */ function _delete(CapacitorChannel $channel, $filter, $func, ?array $args): int { $this->_create($channel); - if ($func === null) $func = "->onDelete"; + if ($func === null) $func = CapacitorChannel::onDelete; func::ensure_func($func, $channel, $args); $onEach = func::_prepare($func); $db = $this->db(); diff --git a/tests/db/sqlite/.gitignore b/tests/db/sqlite/.gitignore index 3d45538..6ab0f32 100644 --- a/tests/db/sqlite/.gitignore +++ b/tests/db/sqlite/.gitignore @@ -1 +1 @@ -/capacitor.db +/capacitor.db* diff --git a/tests/db/sqlite/SqliteStorageTest.php b/tests/db/sqlite/SqliteStorageTest.php index 719528e..8461de0 100644 --- a/tests/db/sqlite/SqliteStorageTest.php +++ b/tests/db/sqlite/SqliteStorageTest.php @@ -148,4 +148,197 @@ class SqliteStorageTest extends TestCase { $capacitor->close(); self::assertTrue(true); } + + function testEachValues() { + # tester que values contient bien toutes les valeurs de la ligne + $storage = new SqliteStorage(__DIR__.'/capacitor.db'); + $capacitor = new Capacitor($storage, new class extends CapacitorChannel { + const NAME = "each_values"; + const COLUMN_DEFINITIONS = [ + "name" => "varchar primary key", + "age" => "integer", + "done" => "integer default 0", + "notes" => "text", + ]; + + function getItemValues($item): ?array { + return [ + "name" => $item["name"], + "age" => $item["age"], + ]; + } + }); + + $capacitor->reset(); + $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $values, ?array $pvalues) { + 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::assertSame([ + "name" => "first", + "age" => 5, + "item" => $item, + ], cl::select($values, ["name", "age", "item"])); + self::assertNull($pvalues); + }); + $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { + 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::assertSame([ + "name" => "first", + "age" => 10, + "done" => 0, + "notes" => null, + "item" => $item, + ], cl::select($values, ["name", "age", "done", "notes", "item"])); + self::assertNotNull($pvalues); + self::assertSame([ + "name" => "first", + "age" => 5, + "done" => 0, + "notes" => null, + "item" => ["name" => "first", "age" => 5], + ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); + }); + + $capacitor->each(null, function($item, ?array $values) { + 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::assertSame([ + "name" => "first", + "age" => 10, + "done" => 0, + "notes" => null, + "item" => $item, + ], cl::select($values, ["name", "age", "done", "notes", "item"])); + return [ + "done" => 1, + "notes" => "modified", + ]; + }); + $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $values, ?array $pvalues) { + 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::assertSame([ + "name" => "first", + "age" => 10, + "done" => 1, + "notes" => "modified", + "item" => $item, + ], cl::select($values, ["name", "age", "done", "notes", "item"])); + self::assertNotNull($pvalues); + self::assertSame([ + "name" => "first", + "age" => 10, + "done" => 1, + "notes" => "modified", + "item" => $item, + ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); + }); + + $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $values, ?array $pvalues) { + 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::assertSame([ + "name" => "first", + "age" => 20, + "done" => 1, + "notes" => "modified", + "item" => $item, + ], cl::select($values, ["name", "age", "done", "notes", "item"])); + self::assertNotNull($pvalues); + self::assertSame([ + "name" => "first", + "age" => 10, + "done" => 1, + "notes" => "modified", + "item" => ["name" => "first", "age" => 10], + ], cl::select($pvalues, ["name", "age", "done", "notes", "item"])); + }); + } + + function testSetItemNull() { + # tester le forçage de $îtem à null pour économiser la place + $storage = new SqliteStorage(__DIR__.'/capacitor.db'); + $capacitor = new Capacitor($storage, new class extends CapacitorChannel { + const NAME = "set_item_null"; + const COLUMN_DEFINITIONS = [ + "name" => "varchar primary key", + "age" => "integer", + "done" => "integer default 0", + "notes" => "text", + ]; + + function getItemValues($item): ?array { + return [ + "name" => $item["name"], + "age" => $item["age"], + ]; + } + }); + + $capacitor->reset(); + $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $values, ?array $pvalues) { + self::assertSame([ + "name" => "first", "age" => 5, + "item" => $item, + ], cl::select($values, ["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) { + self::assertSame([ + "name" => "first", "age" => 10, + "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", + ], cl::select($values, ["name", "age", "item", "item__sum_"])); + self::assertSame([ + "name" => "first", "age" => 5, + "item" => null, "item__sum_" => null, + ], cl::select($pvalues, ["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) { + self::assertSame([ + "name" => "first", "age" => 10, + "item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc", + ], cl::select($values, ["name", "age", "item", "item__sum_"])); + self::assertSame([ + "name" => "first", "age" => 10, + "item" => null, "item__sum_" => null, + ], cl::select($pvalues, ["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) { + self::assertSame([ + "name" => "first", "age" => 20, + "item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5", + ], cl::select($values, ["name", "age", "item", "item__sum_"])); + self::assertSame([ + "name" => "first", "age" => 10, + "item" => null, "item__sum_" => null, + ], cl::select($pvalues, ["name", "age", "item", "item__sum_"])); + return ["item" => null]; + }); + self::assertSame(1, $nbModified); + sleep(1); + } }