From 9e617e7e23dc26590f65b636d1149f7c7c47e1c2 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 10 Jun 2024 21:40:47 -0400 Subject: [PATCH] First cut of ID generation need detection --- src/Document.php | 22 +++++++++++++++++++--- src/Query.php | 7 ++++--- tests/unit/QueryTest.php | 40 ++++++++++++++-------------------------- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Document.php b/src/Document.php index b765f52..ad645d4 100644 --- a/src/Document.php +++ b/src/Document.php @@ -2,8 +2,6 @@ namespace BitBadger\PDODocument; -use BitBadger\PDODocument\Query\Insert; - /** * Functions that apply at a whole document level */ @@ -18,7 +16,25 @@ class Document */ public static function insert(string $tableName, array|object $document): void { - Custom::nonQuery(Query::insert($tableName), Parameters::json(':data', $document)); + $doInsert = fn() => Custom::nonQuery(Query::insert($tableName), Parameters::json(':data', $document)); + + if (Configuration::$autoId == AutoId::None) { + $doInsert(); + return; + } + + $id = Configuration::$idField; + $idProvided = + (is_array( $document) && is_int( $document[$id]) && $document[$id] <> 0) + || (is_array( $document) && is_string($document[$id]) && $document[$id] <> '') + || (is_object($document) && is_int( $document->{$id}) && $document->{$id} <> 0) + || (is_object($document) && is_string($document->{$id}) && $document->{$id} <> ''); + + if ($idProvided) { + $doInsert(); + } else { + Custom::nonQuery(Query::insert($tableName, Configuration::$autoId), Parameters::json(':data', $document)); + } } /** diff --git a/src/Query.php b/src/Query.php index fcdab00..66f10c1 100644 --- a/src/Query.php +++ b/src/Query.php @@ -50,22 +50,23 @@ class Query * Create an `INSERT` statement for a document * * @param string $tableName The name of the table into which the document will be inserted + * @param AutoId|null $autoId The version of automatic ID query to generate (optional, defaults to None) * @return string The `INSERT` statement to insert a document * @throws DocumentException If the database mode is not set */ - public static function insert(string $tableName): string + public static function insert(string $tableName, ?AutoId $autoId = null): string { try { $id = Configuration::$idField; $values = match (Configuration::$mode) { - Mode::SQLite => match (Configuration::$autoId) { + Mode::SQLite => match ($autoId ?? AutoId::None) { AutoId::None => ':data', AutoId::Number => "json_set(:data, '$.$id', " . "(SELECT coalesce(max(data->>'$id'), 0) + 1 FROM $tableName))", AutoId::UUID => "json_set(:data, '$.$id', '" . AutoId::generateUUID() . "')", AutoId::RandomString => "json_set(:data, '$.$id', '" . AutoId::generateRandom() ."')" }, - Mode::PgSQL => match (Configuration::$autoId) { + Mode::PgSQL => match ($autoId ?? AutoId::None) { AutoId::None => ':data', AutoId::Number => ":data || ('{\"$id\":' || " . "(SELECT COALESCE(MAX(data->>'$id'), 0) + 1 FROM $tableName) || '}')", diff --git a/tests/unit/QueryTest.php b/tests/unit/QueryTest.php index 4f985d5..b9e38d4 100644 --- a/tests/unit/QueryTest.php +++ b/tests/unit/QueryTest.php @@ -87,64 +87,56 @@ class QueryTest extends TestCase #[TestDox('Insert succeeds with auto numeric ID for PostgreSQL')] public function testInsertSucceedsWithAutoNumericIdForPostgreSQL(): void { - Configuration::$mode = Mode::PgSQL; - Configuration::$autoId = AutoId::Number; + Configuration::$mode = Mode::PgSQL; try { $this->assertEquals( "INSERT INTO test_tbl VALUES (:data || ('{\"id\":' " . "|| (SELECT COALESCE(MAX(data->>'id'), 0) + 1 FROM test_tbl) || '}'))", - Query::insert('test_tbl'), 'INSERT statement not constructed correctly'); + Query::insert('test_tbl', AutoId::Number), 'INSERT statement not constructed correctly'); } finally { - Configuration::$mode = null; - Configuration::$autoId = AutoId::None; + Configuration::$mode = null; } } #[TestDox('Insert succeeds with auto numeric ID for SQLite')] public function testInsertSucceedsWithAutoNumericIdForSQLite(): void { - Configuration::$mode = Mode::SQLite; - Configuration::$autoId = AutoId::Number; + Configuration::$mode = Mode::SQLite; try { $this->assertEquals( "INSERT INTO test_tbl VALUES (json_set(:data, '$.id', " . "(SELECT coalesce(max(data->>'id'), 0) + 1 FROM test_tbl)))", - Query::insert('test_tbl'), 'INSERT statement not constructed correctly'); + Query::insert('test_tbl', AutoId::Number), 'INSERT statement not constructed correctly'); } finally { - Configuration::$mode = null; - Configuration::$autoId = AutoId::None; + Configuration::$mode = null; } } #[TestDox('Insert succeeds with auto UUID for PostgreSQL')] public function testInsertSucceedsWithAutoUuidForPostgreSQL(): void { - Configuration::$mode = Mode::PgSQL; - Configuration::$autoId = AutoId::UUID; + Configuration::$mode = Mode::PgSQL; try { - $query = Query::insert('test_tbl'); + $query = Query::insert('test_tbl', AutoId::UUID); $this->assertStringStartsWith("INSERT INTO test_tbl VALUES (:data || '{\"id\":\"", $query, 'INSERT statement not constructed correctly'); $this->assertStringEndsWith("\"}')", $query, 'INSERT statement not constructed correctly'); } finally { - Configuration::$mode = null; - Configuration::$autoId = AutoId::None; + Configuration::$mode = null; } } #[TestDox('Insert succeeds with auto UUID for SQLite')] public function testInsertSucceedsWithAutoUuidForSQLite(): void { - Configuration::$mode = Mode::SQLite; - Configuration::$autoId = AutoId::UUID; + Configuration::$mode = Mode::SQLite; try { - $query = Query::insert('test_tbl'); + $query = Query::insert('test_tbl', AutoId::UUID); $this->assertStringStartsWith("INSERT INTO test_tbl VALUES (json_set(:data, '$.id', '", $query, 'INSERT statement not constructed correctly'); $this->assertStringEndsWith("'))", $query, 'INSERT statement not constructed correctly'); } finally { - Configuration::$mode = null; - Configuration::$autoId = AutoId::None; + Configuration::$mode = null; } } @@ -152,10 +144,9 @@ class QueryTest extends TestCase public function testInsertSucceedsWithAutoRandomStringForPostgreSQL(): void { Configuration::$mode = Mode::PgSQL; - Configuration::$autoId = AutoId::RandomString; Configuration::$idStringLength = 8; try { - $query = Query::insert('test_tbl'); + $query = Query::insert('test_tbl', AutoId::RandomString); $this->assertStringStartsWith("INSERT INTO test_tbl VALUES (:data || '{\"id\":\"", $query, 'INSERT statement not constructed correctly'); $this->assertStringEndsWith("\"}')", $query, 'INSERT statement not constructed correctly'); @@ -163,7 +154,6 @@ class QueryTest extends TestCase $this->assertEquals(8, strlen($id), "Generated ID [$id] should have been 8 characters long"); } finally { Configuration::$mode = null; - Configuration::$autoId = AutoId::None; Configuration::$idStringLength = 16; } } @@ -172,9 +162,8 @@ class QueryTest extends TestCase public function testInsertSucceedsWithAutoRandomStringForSQLite(): void { Configuration::$mode = Mode::SQLite; - Configuration::$autoId = AutoId::RandomString; try { - $query = Query::insert('test_tbl'); + $query = Query::insert('test_tbl', AutoId::RandomString); $this->assertStringStartsWith("INSERT INTO test_tbl VALUES (json_set(:data, '$.id', '", $query, 'INSERT statement not constructed correctly'); $this->assertStringEndsWith("'))", $query, 'INSERT statement not constructed correctly'); @@ -182,7 +171,6 @@ class QueryTest extends TestCase $this->assertEquals(16, strlen($id), "Generated ID [$id] should have been 16 characters long"); } finally { Configuration::$mode = null; - Configuration::$autoId = AutoId::None; } }