diff --git a/.idea/php-docker-settings.xml b/.idea/php-docker-settings.xml
index bd786be..047d43d 100644
--- a/.idea/php-docker-settings.xml
+++ b/.idea/php-docker-settings.xml
@@ -17,6 +17,21 @@
+
+
+
+
+
+
+
+
diff --git a/php/src/db/Capacitor.php b/php/src/db/Capacitor.php
index f136fae..de8d1a1 100644
--- a/php/src/db/Capacitor.php
+++ b/php/src/db/Capacitor.php
@@ -21,6 +21,14 @@ abstract class Capacitor {
return $this;
}
+ function newChannel($channel): CapacitorChannel {
+ if (!($channel instanceof CapacitorChannel)) {
+ if (!is_array($channel)) $channel = ["name" => $channel];
+ $channel = new CapacitorChannel($channel);
+ }
+ return $channel->initCapacitor($this);
+ }
+
# les définitions sont par défaut pour MariaDB/MySQL
const CDATA_DEFINITION = "mediumtext";
const CSUM_DEFINITION = "varchar(40)";
@@ -179,6 +187,9 @@ abstract class Capacitor {
else return $rowIds;
}
+ #############################################################################
+ # Migration et metadata
+
abstract protected function tableExists(string $tableName): bool;
const METADATA_TABLE = "_metadata";
@@ -216,6 +227,9 @@ abstract class Capacitor {
abstract function getMigration(CapacitorChannel $channel): _migration;
+ #############################################################################
+ # Catalogue
+
const CATALOG_TABLE = "_channels";
const CATALOG_COLS = [
"name" => "varchar not null primary key",
@@ -259,21 +273,7 @@ abstract class Capacitor {
return $raw !== null;
}
- function _create(CapacitorChannel $channel): void {
- if (!$this->tableExists($channel->getTableName())) {
- $this->prepareMetadata();
- $this->getMigration($channel)->migrate($this->db());
- $this->afterCreate($channel);
- }
- }
-
- function create(CapacitorChannel $channel, bool $force=false): void {
- if ($force || !$channel->isCreated()) {
- $channel->ensureSetup();
- $this->_create($channel);
- $channel->setCreated();
- }
- }
+ #############################################################################
protected function afterCreate(CapacitorChannel $channel): void {
$db = $this->db();
@@ -281,6 +281,20 @@ abstract class Capacitor {
$db->exec($this->addToCatalogSql($channel));
}
+ function create(CapacitorChannel $channel): void {
+ $this->prepareMetadata();
+ $this->getMigration($channel)->migrate($this->db());
+ $this->afterCreate($channel);
+ }
+
+ function autocreate(CapacitorChannel $channel, bool $force=false): void {
+ if ($force || !$channel->isCreated()) {
+ $channel->ensureSetup();
+ $this->create($channel);
+ $channel->setCreated();
+ }
+ }
+
/** tester si le canal spécifié existe */
function exists(CapacitorChannel $channel): bool {
return $this->tableExists($channel->getTableName());
@@ -313,7 +327,7 @@ abstract class Capacitor {
$channel->getTableName(),
]);
$channel->setCreated(false);
- if ($recreate) $this->create($channel);
+ if ($recreate) $this->autocreate($channel);
}
/**
@@ -341,7 +355,8 @@ abstract class Capacitor {
* déjà à l'identique dans le canal
*/
function charge(CapacitorChannel $channel, $item, $func=null, ?array $args=null, ?array &$row=null): int {
- $this->create($channel);
+ $channel->initCapacitor($this);
+
$tableName = $channel->getTableName();
$db = $this->db();
$args ??= [];
@@ -488,7 +503,8 @@ abstract class Capacitor {
* fournie
*/
function discharge(CapacitorChannel $channel, bool $reset=true): Traversable {
- $this->create($channel);
+ $channel->initCapacitor($this);
+
$raws = $this->db()->all([
"select item__",
"from" => $channel->getTableName(),
@@ -537,8 +553,9 @@ abstract class Capacitor {
/** indiquer le nombre d'éléments du canal spécifié */
function count(CapacitorChannel $channel, $filter): int {
- $this->create($channel);
+ $channel->initCapacitor($this);
$this->verifixFilter($channel, $filter);
+
return $this->db()->get([
"select count(*)",
"from" => $channel->getTableName(),
@@ -552,9 +569,9 @@ abstract class Capacitor {
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
*/
function one(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): ?array {
- if ($filter === null) throw ValueException::null("filter");
- $this->create($channel);
+ $channel->initCapacitor($this);
$this->verifixFilter($channel, $filter);
+
$raw = $this->db()->one(cl::merge([
"select",
"from" => $channel->getTableName(),
@@ -569,8 +586,9 @@ abstract class Capacitor {
* si $filter n'est pas un tableau, il est transformé en ["id_" => $filter]
*/
function all(CapacitorChannel $channel, $filter, ?array $mergeQuery=null): Traversable {
- $this->create($channel);
+ $channel->initCapacitor($this);
$this->verifixFilter($channel, $filter);
+
$raws = $this->db()->all(cl::merge([
"select",
"from" => $channel->getTableName(),
@@ -594,7 +612,8 @@ abstract class Capacitor {
* @return int le nombre de lignes parcourues
*/
function each(CapacitorChannel $channel, $filter, $func=null, ?array $args=null, ?array $mergeQuery=null, ?int &$nbUpdated=null): int {
- $this->create($channel);
+ $channel->initCapacitor($this);
+
if ($func === null) $func = CapacitorChannel::onEach;
$onEach = func::with($func)->bind($channel);
$db = $this->db();
@@ -660,7 +679,8 @@ abstract class Capacitor {
* @return int le nombre de lignes parcourues
*/
function delete(CapacitorChannel $channel, $filter, $func=null, ?array $args=null): int {
- $this->create($channel);
+ $channel->initCapacitor($this);
+
if ($func === null) $func = CapacitorChannel::onDelete;
$onDelete = func::with($func)->bind($channel);
$db = $this->db();
diff --git a/php/src/db/CapacitorChannel.php b/php/src/db/CapacitorChannel.php
index f3aa911..8151abf 100644
--- a/php/src/db/CapacitorChannel.php
+++ b/php/src/db/CapacitorChannel.php
@@ -55,9 +55,8 @@ class CapacitorChannel implements ITransactor {
return $eachCommitThreshold;
}
- function __construct(?Capacitor $capacitor=null, ?array $params=null) {
- $capacitor ??= $params["storage"] ?? null;
- $this->capacitor = $capacitor;
+ function __construct(?array $params=null) {
+ $this->capacitor = null;
$name = $params["name"] ?? static::NAME;
$tableName = $params["tableName"] ?? static::TABLE_NAME;
@@ -65,8 +64,9 @@ class CapacitorChannel implements ITransactor {
$this->name = $name;
$this->tableName = $tableName;
- $autocreate = $params["autocreate"] ?? null;
- $autocreate ??= !app::get()->isProductionMode();
+ #$autocreate = $params["autocreate"] ?? null;
+ #$autocreate ??= !app::get()->isProductionMode();
+ $autocreate = true; #XXX
$this->created = !$autocreate;
$this->setup = false;
@@ -423,8 +423,9 @@ class CapacitorChannel implements ITransactor {
return $this->capacitor;
}
- function initCapacitor(Capacitor $capacitor): self {
- $this->capacitor = $capacitor;
+ function initCapacitor(Capacitor $capacitor, bool $autocreate=true): self {
+ if ($this->capacitor === null) $this->capacitor = $capacitor;
+ if ($autocreate) $this->capacitor->autocreate($this);
return $this;
}
@@ -530,7 +531,7 @@ class CapacitorChannel implements ITransactor {
}
function charge($item, $func=null, ?array $args=null, ?array &$row=null): int {
- return $this->capacitor->charge($item, $func, $args, $row);
+ return $this->capacitor->charge($this, $item, $func, $args, $row);
}
function chargeAll(?iterable $items, $func=null, ?array $args=null): int {
diff --git a/php/tests/db/sqlite/ChannelMigrationTest.php b/php/tests/db/sqlite/ChannelMigrationTest.php
index e399d15..799cf18 100644
--- a/php/tests/db/sqlite/ChannelMigrationTest.php
+++ b/php/tests/db/sqlite/ChannelMigrationTest.php
@@ -30,23 +30,23 @@ class ChannelMigrationTest extends TestCase {
}
function testMigration() {
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
$data = [
["first", "premier", new DateTime(), new DateTime(), 15],
["second", "deuxieme", new DateTime(), new DateTime(), 15],
];
- new Capacitor($storage, $channel = new MyChannel());
+ $capacitor->newChannel($channel = new MyChannel());
$channel->reset(true);
$this->addData($channel, $data);
- new Capacitor($storage, $channel = new MyChannelV2());
+ $capacitor->newChannel($channel = new MyChannelV2());
$this->addData($channel, $data);
- new Capacitor($storage, $channel = new MyChannelV3());
+ $capacitor->newChannel($channel = new MyChannelV3());
$this->addData($channel, $data);
- $sql = $channel->getCapacitor()->getCreateChannelSql();
+ $sql = $channel->getCreateSql();
$class = MyChannelV3::class;
$expected = <<newChannel($channel = new MyIndexChannel());
$channel->reset(true);
$channel->chargeAll($data);
- $sql = $channel->getCapacitor()->getCreateChannelSql();
+ $sql = $channel->getCreateSql();
$class = MyIndexChannel::class;
$expected = <<newChannel(null);
$this->_testChargeStrings($capacitor, $channel);
+ self::Txx(cl::all($capacitor->discharge($channel, false)));
+
+ $channel = $capacitor->newChannel("strings");
+ $this->_testChargeStrings($capacitor, $channel);
+ self::Txx(cl::all($capacitor->discharge($channel, false)));
+
$capacitor->close();
+ self::assertTrue(true);
}
function testChargeArrays() {
$capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $channel = new class extends CapacitorChannel {
+
+ $channel = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "arrays";
const COLUMN_DEFINITIONS = ["id" => "integer"];
function getItemValues($item): ?array {
return ["id" => $item["id"] ?? null];
}
- };
+ });
+ $this->_testChargeArrays($capacitor, $channel);
+ self::Txx(cl::all($capacitor->discharge($channel, false)));
- $this->_testChargeStrings($capacitor, "strings");
- $this->_testChargeArrays($capacitor, "arrays");
$capacitor->close();
+ self::assertTrue(true);
}
function testEach() {
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $each = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "each";
const COLUMN_DEFINITIONS = [
"age" => "integer",
@@ -68,11 +78,11 @@ class SqliteCapacitorTest extends TestCase {
}
});
- $capacitor->reset();
- $capacitor->charge(["name" => "first", "age" => 5]);
- $capacitor->charge(["name" => "second", "age" => 10]);
- $capacitor->charge(["name" => "third", "age" => 15]);
- $capacitor->charge(["name" => "fourth", "age" => 20]);
+ $capacitor->reset($each);
+ $capacitor->charge($each, ["name" => "first", "age" => 5]);
+ $capacitor->charge($each, ["name" => "second", "age" => 10]);
+ $capacitor->charge($each, ["name" => "third", "age" => 15]);
+ $capacitor->charge($each, ["name" => "fourth", "age" => 20]);
$setDone = function ($row, $suffix=null) {
$item = $row["item"];
@@ -83,17 +93,18 @@ class SqliteCapacitorTest extends TestCase {
}
return $updates;
};
- $capacitor->each(["age" => [">", 10]], $setDone, ["++"]);
- $capacitor->each(["done" => 0], $setDone);
+ $capacitor->each($each, ["age" => [">", 10]], $setDone, ["++"]);
+ $capacitor->each($each, ["done" => 0], $setDone);
+
+ self::Txx(cl::all($capacitor->discharge($each, false)));
- self::Txx(cl::all($capacitor->discharge(false)));
$capacitor->close();
self::assertTrue(true);
}
function testPrimayKey() {
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $channel = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "pk";
const COLUMN_DEFINITIONS = [
"id_" => "varchar primary key",
@@ -107,21 +118,23 @@ class SqliteCapacitorTest extends TestCase {
}
});
- $capacitor->charge(["numero" => "a", "name" => "first", "age" => 5]);
- $capacitor->charge(["numero" => "b", "name" => "second", "age" => 10]);
- $capacitor->charge(["numero" => "c", "name" => "third", "age" => 15]);
- $capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 20]);
+ $capacitor->charge($channel, ["numero" => "a", "name" => "first", "age" => 5]);
+ $capacitor->charge($channel, ["numero" => "b", "name" => "second", "age" => 10]);
+ $capacitor->charge($channel, ["numero" => "c", "name" => "third", "age" => 15]);
+ $capacitor->charge($channel, ["numero" => "d", "name" => "fourth", "age" => 20]);
sleep(2);
- $capacitor->charge(["numero" => "b", "name" => "second", "age" => 100]);
- $capacitor->charge(["numero" => "d", "name" => "fourth", "age" => 200]);
+ $capacitor->charge($channel, ["numero" => "b", "name" => "second", "age" => 100]);
+ $capacitor->charge($channel, ["numero" => "d", "name" => "fourth", "age" => 200]);
+
+ self::Txx(cl::all($capacitor->discharge($channel, false)));
$capacitor->close();
self::assertTrue(true);
}
function testSum() {
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $channel = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "sum";
const COLUMN_DEFINITIONS = [
"a__" => "varchar",
@@ -137,19 +150,17 @@ class SqliteCapacitorTest extends TestCase {
}
});
- $capacitor->reset();
- $capacitor->charge(["a" => null, "b" => null]);
- $capacitor->charge(["a" => "first", "b" => "second"]);
+ $capacitor->reset($channel);
+ $capacitor->charge($channel, ["a" => null, "b" => null]);
+ $capacitor->charge($channel, ["a" => "first", "b" => "second"]);
self::Txx("=== all");
- /** @var Sqlite $sqlite */
- $sqlite = $capacitor->getStorage()->db();
- self::Txx(cl::all($sqlite->all([
+ self::Txx(cl::all($capacitor->db()->all([
"select",
- "from" => $capacitor->getChannel()->getTableName(),
+ "from" => $channel->getTableName(),
])));
self::Txx("=== each");
- $capacitor->each(null, function ($row) {
+ $capacitor->each($channel, null, function ($row) {
self::Txx($row);
});
@@ -159,8 +170,8 @@ class SqliteCapacitorTest extends TestCase {
function testEachValues() {
# tester que values contient bien toutes les valeurs de la ligne
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $channel = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "each_values";
const COLUMN_DEFINITIONS = [
"name" => "varchar primary key",
@@ -177,8 +188,8 @@ class SqliteCapacitorTest extends TestCase {
}
});
- $capacitor->reset();
- $capacitor->charge(["name" => "first", "age" => 5], function($item, ?array $row, ?array $prow) {
+ $capacitor->reset($channel);
+ $capacitor->charge($channel, ["name" => "first", "age" => 5], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(5, $item["age"]);
self::assertnotnull($row);
@@ -190,7 +201,7 @@ class SqliteCapacitorTest extends TestCase {
], cl::select($row, ["name", "age", "item"]));
self::assertNull($prow);
});
- $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
+ $capacitor->charge($channel, ["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
self::assertnotnull($row);
@@ -212,7 +223,7 @@ class SqliteCapacitorTest extends TestCase {
], cl::select($prow, ["name", "age", "done", "notes", "item"]));
});
- $capacitor->each(null, function(array $row) {
+ $capacitor->each($channel, null, function(array $row) {
$item = $row["item"];
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
@@ -230,7 +241,7 @@ class SqliteCapacitorTest extends TestCase {
"notes" => "modified",
];
});
- $capacitor->charge(["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
+ $capacitor->charge($channel, ["name" => "first", "age" => 10], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(10, $item["age"]);
self::assertnotnull($row);
@@ -252,7 +263,7 @@ class SqliteCapacitorTest extends TestCase {
], cl::select($prow, ["name", "age", "done", "notes", "item"]));
});
- $capacitor->charge(["name" => "first", "age" => 20], function($item, ?array $row, ?array $prow) {
+ $capacitor->charge($channel, ["name" => "first", "age" => 20], function($item, ?array $row, ?array $prow) {
self::assertSame("first", $item["name"]);
self::assertSame(20, $item["age"]);
self::assertnotnull($row);
@@ -277,8 +288,8 @@ class SqliteCapacitorTest extends TestCase {
function testSetItemNull() {
# tester le forçage de $îtem à null pour économiser la place
- $storage = new SqliteCapacitor(__DIR__.'/capacitor.db');
- $capacitor = new Capacitor($storage, new class extends CapacitorChannel {
+ $capacitor = new SqliteCapacitor(__DIR__.'/capacitor.db');
+ $channel = $capacitor->newChannel(new class extends CapacitorChannel {
const NAME = "set_item_null";
const COLUMN_DEFINITIONS = [
"name" => "varchar primary key",
@@ -295,8 +306,8 @@ class SqliteCapacitorTest extends TestCase {
}
});
- $capacitor->reset();
- $nbModified = $capacitor->charge(["name" => "first", "age" => 5], function ($item, ?array $row, ?array $prow) {
+ $capacitor->reset($channel);
+ $nbModified = $capacitor->charge($channel, ["name" => "first", "age" => 5], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 5,
"item" => $item,
@@ -307,7 +318,7 @@ class SqliteCapacitorTest extends TestCase {
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 $row, ?array $prow) {
+ $nbModified = $capacitor->charge($channel, ["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
@@ -322,7 +333,7 @@ class SqliteCapacitorTest extends TestCase {
sleep(1);
# pas de modification ici
- $nbModified = $capacitor->charge(["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) {
+ $nbModified = $capacitor->charge($channel, ["name" => "first", "age" => 10], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 10,
"item" => $item, "item__sum_" => "9181336dfca20c86313d6065d89aa2ad5070b0fc",
@@ -336,7 +347,7 @@ class SqliteCapacitorTest extends TestCase {
self::assertSame(0, $nbModified);
sleep(1);
- $nbModified = $capacitor->charge(["name" => "first", "age" => 20], function ($item, ?array $row, ?array $prow) {
+ $nbModified = $capacitor->charge($channel, ["name" => "first", "age" => 20], function ($item, ?array $row, ?array $prow) {
self::assertSame([
"name" => "first", "age" => 20,
"item" => $item, "item__sum_" => "001b91982b4e0883b75428c0eb28573a5dc5f7a5",