diff --git a/composer.json b/composer.json index 0cd85ed..3c33d3e 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "rss": "https://git.bitbadger.solutions/bit-badger/pdo-document.rss" }, "require": { - "php": ">=8.3", + "php": ">=8.1", "netresearch/jsonmapper": "^4", "ext-pdo": "*" }, diff --git a/composer.lock b/composer.lock index c9a364a..5cb3710 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ca79f450e8e715ad61ba3581734c0fe7", + "content-hash": "f4563891566be8872ae85552261303bd", "packages": [ { "name": "netresearch/jsonmapper", @@ -1697,7 +1697,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=8.3", + "php": ">=8.1", "ext-pdo": "*" }, "platform-dev": [], diff --git a/src/Count.php b/src/Count.php index df91c72..62b2513 100644 --- a/src/Count.php +++ b/src/Count.php @@ -26,14 +26,14 @@ class Count * * @param string $tableName The name of the table in which documents should be counted * @param array|Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return int The count of documents matching the field comparison * @throws DocumentException If one is encountered */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): int + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): int { $namedFields = Parameters::nameFields($fields); - return Custom::scalar(Query\Count::byFields($tableName, $namedFields, $conjunction), + return Custom::scalar(Query\Count::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, []), new CountMapper()); } } diff --git a/src/Delete.php b/src/Delete.php index 00a408f..ee4fcb8 100644 --- a/src/Delete.php +++ b/src/Delete.php @@ -24,13 +24,13 @@ class Delete * * @param string $tableName The table from which documents should be deleted * @param array|Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @throws DocumentException If any is encountered */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): void + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): void { $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\Delete::byFields($tableName, $namedFields, $conjunction), + Custom::nonQuery(Query\Delete::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, [])); } } diff --git a/src/Exists.php b/src/Exists.php index 11548d4..bc9e1a5 100644 --- a/src/Exists.php +++ b/src/Exists.php @@ -27,14 +27,14 @@ class Exists * * @param string $tableName The name of the table in which document existence should be determined * @param Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return bool True if any documents match the field comparison, false if not * @throws DocumentException If any is encountered */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): bool + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): bool { $namedFields = Parameters::nameFields($fields); - return Custom::scalar(Query\Exists::byFields($tableName, $namedFields, $conjunction), + return Custom::scalar(Query\Exists::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, []), new ExistsMapper()); } } diff --git a/src/FieldMatch.php b/src/FieldMatch.php new file mode 100644 index 0000000..6cb7b6d --- /dev/null +++ b/src/FieldMatch.php @@ -0,0 +1,28 @@ + 'AND', + FieldMatch::Any => 'OR' + }; + } +} diff --git a/src/Find.php b/src/Find.php index a3bb007..aae5b35 100644 --- a/src/Find.php +++ b/src/Find.php @@ -45,15 +45,15 @@ class Find * @param string $tableName The table from which documents should be retrieved * @param array|Field[] $fields The field comparison to match * @param class-string $className The name of the class to be retrieved - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return DocumentList A list of documents matching the given field comparison * @throws DocumentException If any is encountered */ public static function byFields(string $tableName, array $fields, string $className, - string $conjunction = 'AND'): DocumentList + ?FieldMatch $match = null): DocumentList { $namedFields = Parameters::nameFields($fields); - return Custom::list(Query\Find::byFields($tableName, $namedFields, $conjunction), + return Custom::list(Query\Find::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, []), new DocumentMapper($className)); } @@ -64,15 +64,15 @@ class Find * @param string $tableName The table from which the document should be retrieved * @param array|Field[] $fields The field comparison to match * @param class-string $className The name of the class to be retrieved - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return false|TDoc The first document if any matches are found, false otherwise * @throws DocumentException If any is encountered */ public static function firstByFields(string $tableName, array $fields, string $className, - string $conjunction = 'AND'): mixed + ?FieldMatch $match = null): mixed { $namedFields = Parameters::nameFields($fields); - return Custom::single(Query\Find::byFields($tableName, $namedFields, $conjunction), + return Custom::single(Query\Find::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, []), new DocumentMapper($className)); } } diff --git a/src/Patch.php b/src/Patch.php index 98e5378..75c7983 100644 --- a/src/Patch.php +++ b/src/Patch.php @@ -27,14 +27,14 @@ class Patch * @param string $tableName The table in which documents should be patched * @param array|Field[] $fields The field comparison to match * @param array|object $patch The object with which the documents should be patched (will be JSON-encoded) - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @throws DocumentException If any is encountered */ public static function byFields(string $tableName, array $fields, array|object $patch, - string $conjunction = 'AND'): void + ?FieldMatch $match = null): void { $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $conjunction), + Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $match), Parameters::addFields($namedFields, Parameters::json(':data', $patch))); } } diff --git a/src/Query.php b/src/Query.php index 495315a..c6cc83f 100644 --- a/src/Query.php +++ b/src/Query.php @@ -22,12 +22,14 @@ class Query * Create a WHERE clause fragment to implement a comparison on fields in a JSON document * * @param Field[] $fields The field comparison to generate - * @param string $conjunction How to join multiple conditions (optional; defaults to AND) + * @param FieldMatch|null $match How to join multiple conditions (optional; defaults to All) * @return string The WHERE clause fragment matching the given fields and parameter + * @throws DocumentException If the database mode has not been set */ - public static function whereByFields(array $fields, string $conjunction = 'AND'): string + public static function whereByFields(array $fields, ?FieldMatch $match = null): string { - return implode(" $conjunction ", array_map(fn($it) => $it->toWhere(), $fields)); + return implode(' ' . ($match ?? FieldMatch::All)->toString() . ' ', + array_map(fn($it) => $it->toWhere(), $fields)); } /** @@ -35,6 +37,7 @@ class Query * * @param string $paramName The parameter name where the value of the ID will be provided (optional; default @id) * @return string The WHERE clause fragment to match by ID + * @throws DocumentException If the database mode has not been set */ public static function whereById(string $paramName = ':id'): string { @@ -69,6 +72,7 @@ class Query * * @param string $tableName The name of the table in which the document should be updated * @return string The UPDATE query for the document + * @throws DocumentException If the database mode has not been set */ public static function update(string $tableName): string { diff --git a/src/Query/Count.php b/src/Query/Count.php index 87c47e3..8727e96 100644 --- a/src/Query/Count.php +++ b/src/Query/Count.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Field, Query}; +use BitBadger\PDODocument\{DocumentException, Field, FieldMatch, Query}; /** * Queries for counting documents @@ -25,11 +25,12 @@ class Count * * @param string $tableName The name of the table in which documents should be counted * @param Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to join multiple conditions (optional; defaults to All) * @return string The query to count documents using a field comparison + * @throws DocumentException If the database mode has not been set */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): string + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): string { - return self::all($tableName) . ' WHERE ' . Query::whereByFields($fields, $conjunction); + return self::all($tableName) . ' WHERE ' . Query::whereByFields($fields, $match); } } diff --git a/src/Query/Delete.php b/src/Query/Delete.php index 84bd2a0..a8303ab 100644 --- a/src/Query/Delete.php +++ b/src/Query/Delete.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Field, Query}; +use BitBadger\PDODocument\{DocumentException, Field, FieldMatch, Query}; /** * Queries to delete documents @@ -14,6 +14,7 @@ class Delete * * @param string $tableName The name of the table from which a document should be deleted * @return string The DELETE statement to delete a document by its ID + * @throws DocumentException If the database mode has not been set */ public static function byId(string $tableName): string { @@ -25,11 +26,12 @@ class Delete * * @param string $tableName The name of the table from which documents should be deleted * @param Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return string The DELETE statement to delete documents via field comparison + * @throws DocumentException If the database mode has not been set */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): string + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): string { - return "DELETE FROM $tableName WHERE " . Query::whereByFields($fields, $conjunction); + return "DELETE FROM $tableName WHERE " . Query::whereByFields($fields, $match); } } diff --git a/src/Query/Exists.php b/src/Query/Exists.php index a7750b1..6ff86f4 100644 --- a/src/Query/Exists.php +++ b/src/Query/Exists.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Field, Query}; +use BitBadger\PDODocument\{DocumentException, Field, FieldMatch, Query}; /** * Queries to determine document existence @@ -26,6 +26,7 @@ class Exists * * @param string $tableName The name of the table in which document existence should be checked * @return string The query to determine document existence by ID + * @throws DocumentException If the database mode has not been set */ public static function byId(string $tableName): string { @@ -37,11 +38,12 @@ class Exists * * @param string $tableName The name of the table in which document existence should be checked * @param Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return string The query to determine document existence by field comparison + * @throws DocumentException If the database mode has not been set */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): string + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): string { - return self::query($tableName, Query::whereByFields($fields, $conjunction)); + return self::query($tableName, Query::whereByFields($fields, $match)); } } diff --git a/src/Query/Find.php b/src/Query/Find.php index 2e65385..824e8f3 100644 --- a/src/Query/Find.php +++ b/src/Query/Find.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Field, Query}; +use BitBadger\PDODocument\{DocumentException, Field, FieldMatch, Query}; /** * Queries for retrieving documents @@ -14,6 +14,7 @@ class Find * * @param string $tableName The name of the table from which a document should be retrieved * @return string The SELECT statement to retrieve a document by its ID + * @throws DocumentException If the database mode has not been set */ public static function byId(string $tableName): string { @@ -25,11 +26,12 @@ class Find * * @param string $tableName The name of the table from which documents should be retrieved * @param Field[] $fields The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return string The SELECT statement to retrieve documents by field comparison + * @throws DocumentException If the database mode has not been set */ - public static function byFields(string $tableName, array $fields, string $conjunction = 'AND'): string + public static function byFields(string $tableName, array $fields, ?FieldMatch $match = null): string { - return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereByFields($fields, $conjunction); + return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereByFields($fields, $match); } } diff --git a/src/Query/Patch.php b/src/Query/Patch.php index 6b1ab76..50a696b 100644 --- a/src/Query/Patch.php +++ b/src/Query/Patch.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Configuration, DocumentException, Field, Mode, Query}; +use BitBadger\PDODocument\{Configuration, DocumentException, Field, FieldMatch, Mode, Query}; /** * Queries to perform partial updates on documents @@ -44,12 +44,12 @@ class Patch * * @param string $tableName The name of the table in which documents should be patched * @param array|Field[] $field The field comparison to match - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return string The query to patch documents via field comparison * @throws DocumentException If the database mode has not been set */ - public static function byFields(string $tableName, array $field, string $conjunction = 'AND'): string + public static function byFields(string $tableName, array $field, ?FieldMatch $match = null): string { - return self::update($tableName, Query::whereByFields($field, $conjunction)); + return self::update($tableName, Query::whereByFields($field, $match)); } } diff --git a/src/Query/RemoveFields.php b/src/Query/RemoveFields.php index 0e3fea0..c668abf 100644 --- a/src/Query/RemoveFields.php +++ b/src/Query/RemoveFields.php @@ -2,7 +2,7 @@ namespace BitBadger\PDODocument\Query; -use BitBadger\PDODocument\{Configuration, DocumentException, Field, Mode, Query}; +use BitBadger\PDODocument\{Configuration, DocumentException, Field, FieldMatch, Mode, Query}; /** * Queries to remove fields from documents @@ -54,13 +54,13 @@ class RemoveFields * @param string $tableName The name of the table in which documents should be manipulated * @param array|Field[] $fields The field comparison to match * @param array $parameters The parameter list for the query - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @return string The UPDATE statement to remove fields from documents via field comparison * @throws DocumentException If the database mode has not been set */ public static function byFields(string $tableName, array $fields, array $parameters, - string $conjunction = 'AND'): string + ?FieldMatch $match = null): string { - return self::update($tableName, $parameters, Query::whereByFields($fields, $conjunction)); + return self::update($tableName, $parameters, Query::whereByFields($fields, $match)); } } diff --git a/src/RemoveFields.php b/src/RemoveFields.php index b32e2f4..3933e7d 100644 --- a/src/RemoveFields.php +++ b/src/RemoveFields.php @@ -28,15 +28,15 @@ class RemoveFields * @param string $tableName The table in which documents should have fields removed * @param array|Field[] $fields The field comparison to match * @param array|string[] $fieldNames The names of the fields to be removed - * @param string $conjunction How to handle multiple conditions (optional; defaults to `AND`) + * @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @throws DocumentException If any is encountered */ public static function byFields(string $tableName, array $fields, array $fieldNames, - string $conjunction = 'AND'): void + ?FieldMatch $match = null): void { $nameParams = Parameters::fieldNames(':name', $fieldNames); $namedFields = Parameters::nameFields($fields); - Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $conjunction), + Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $match), Parameters::addFields($namedFields, $nameParams)); } } diff --git a/tests/unit/FieldMatchTest.php b/tests/unit/FieldMatchTest.php new file mode 100644 index 0000000..9fd8942 --- /dev/null +++ b/tests/unit/FieldMatchTest.php @@ -0,0 +1,22 @@ +assertEquals('AND', FieldMatch::All->toString(), 'All should have returned AND'); + } + + public function testToStringSucceedsForAny(): void + { + $this->assertEquals('OR', FieldMatch::Any->toString(), 'Any should have returned OR'); + } +} diff --git a/tests/unit/Query/FindTest.php b/tests/unit/Query/FindTest.php index 5b2fb9a..10e7d73 100644 --- a/tests/unit/Query/FindTest.php +++ b/tests/unit/Query/FindTest.php @@ -2,7 +2,7 @@ namespace Test\Unit\Query; -use BitBadger\PDODocument\{Configuration, Field, Mode}; +use BitBadger\PDODocument\{Configuration, Field, FieldMatch, Mode}; use BitBadger\PDODocument\Query\Find; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -32,7 +32,8 @@ class FindTest extends TestCase public function testByFieldsSucceeds(): void { $this->assertEquals("SELECT data FROM there WHERE data->>'active' = :act OR data->>'locked' = :lock", - Find::byFields('there', [Field::EQ('active', true, ':act'), Field::EQ('locked', true, ':lock')], 'OR'), + Find::byFields('there', [Field::EQ('active', true, ':act'), Field::EQ('locked', true, ':lock')], + FieldMatch::Any), 'SELECT query not generated correctly'); } } diff --git a/tests/unit/QueryTest.php b/tests/unit/QueryTest.php index b3ac80b..d92d4b7 100644 --- a/tests/unit/QueryTest.php +++ b/tests/unit/QueryTest.php @@ -2,7 +2,7 @@ namespace Test\Unit; -use BitBadger\PDODocument\{Configuration, Field, Mode, Query}; +use BitBadger\PDODocument\{Configuration, Field, FieldMatch, Mode, Query}; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; @@ -33,17 +33,18 @@ class QueryTest extends TestCase Query::whereByFields([Field::LE('test_field', '', ':it')]), 'WHERE fragment not constructed correctly'); } - public function testWhereByFieldsSucceedsForMultipleFields(): void + public function testWhereByFieldsSucceedsForMultipleFieldsAll(): void { $this->assertEquals("data->>'test_field' <= :it AND data->>'other_field' = :other", Query::whereByFields([Field::LE('test_field', '', ':it'), Field::EQ('other_field', '', ':other')]), 'WHERE fragment not constructed correctly'); } - public function testWhereByFieldsSucceedsForMultipleFieldsWithOr(): void + public function testWhereByFieldsSucceedsForMultipleFieldsAny(): void { $this->assertEquals("data->>'test_field' <= :it OR data->>'other_field' = :other", - Query::whereByFields([Field::LE('test_field', '', ':it'), Field::EQ('other_field', '', ':other')], 'OR'), + Query::whereByFields([Field::LE('test_field', '', ':it'), Field::EQ('other_field', '', ':other')], + FieldMatch::Any), 'WHERE fragment not constructed correctly'); }