3 Commits

Author SHA1 Message Date
478684621c Add pjson support 2024-06-29 11:46:16 -04:00
50854275a8 Update pkg description 2024-06-25 10:42:26 -04:00
0c9490e394 Use Option for single doc queries 2024-06-24 22:04:11 -04:00
20 changed files with 409 additions and 180 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "bit-badger/pdo-document", "name": "bit-badger/pdo-document",
"description": "Treat SQLite (and soon PostgreSQL) as a document store", "description": "Treat SQLite and PostgreSQL as document stores",
"keywords": ["database", "document", "sqlite", "pdo"], "keywords": ["database", "document", "sqlite", "pdo", "postgresql"],
"license": "MIT", "license": "MIT",
"authors": [ "authors": [
{ {
@@ -19,10 +19,12 @@
"require": { "require": {
"php": ">=8.2", "php": ">=8.2",
"netresearch/jsonmapper": "^4", "netresearch/jsonmapper": "^4",
"ext-pdo": "*" "ext-pdo": "*",
"phpoption/phpoption": "^1.9"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11" "phpunit/phpunit": "^11",
"square/pjson": "^0.5.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
@@ -33,6 +35,7 @@
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"Test\\": "./tests",
"Test\\Unit\\": "./tests/unit", "Test\\Unit\\": "./tests/unit",
"Test\\Integration\\": "./tests/integration", "Test\\Integration\\": "./tests/integration",
"Test\\Integration\\PostgreSQL\\": "./tests/integration/postgresql", "Test\\Integration\\PostgreSQL\\": "./tests/integration/postgresql",
@@ -42,4 +45,4 @@
"archive": { "archive": {
"exclude": [ "/tests", "/.gitattributes", "/.gitignore", "/.git", "/composer.lock" ] "exclude": [ "/tests", "/.gitattributes", "/.gitignore", "/.git", "/composer.lock" ]
} }
} }

194
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "20bf0d96304e429b431535d05ff4585a", "content-hash": "dc897c0ab21d662a65b3818331edd2f2",
"packages": [ "packages": [
{ {
"name": "netresearch/jsonmapper", "name": "netresearch/jsonmapper",
@@ -56,21 +56,96 @@
"source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1" "source": "https://github.com/cweiske/jsonmapper/tree/v4.4.1"
}, },
"time": "2024-01-31T06:18:54+00:00" "time": "2024-01-31T06:18:54+00:00"
},
{
"name": "phpoption/phpoption",
"version": "1.9.2",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
"reference": "80735db690fe4fc5c76dfa7f9b770634285fa820"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820",
"reference": "80735db690fe4fc5c76dfa7f9b770634285fa820",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": true
},
"branch-alias": {
"dev-master": "1.9-dev"
}
},
"autoload": {
"psr-4": {
"PhpOption\\": "src/PhpOption/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Johannes M. Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "https://github.com/schmittjoh"
},
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
}
],
"description": "Option Type for PHP",
"keywords": [
"language",
"option",
"php",
"type"
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
"source": "https://github.com/schmittjoh/php-option/tree/1.9.2"
},
"funding": [
{
"url": "https://github.com/GrahamCampbell",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
"type": "tidelift"
}
],
"time": "2023-11-12T21:59:55+00:00"
} }
], ],
"packages-dev": [ "packages-dev": [
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
"version": "1.11.1", "version": "1.12.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/DeepCopy.git", "url": "https://github.com/myclabs/DeepCopy.git",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -78,11 +153,12 @@
}, },
"conflict": { "conflict": {
"doctrine/collections": "<1.6.8", "doctrine/collections": "<1.6.8",
"doctrine/common": "<2.13.3 || >=3,<3.2.2" "doctrine/common": "<2.13.3 || >=3 <3.2.2"
}, },
"require-dev": { "require-dev": {
"doctrine/collections": "^1.6.8", "doctrine/collections": "^1.6.8",
"doctrine/common": "^2.13.3 || ^3.2.2", "doctrine/common": "^2.13.3 || ^3.2.2",
"phpspec/prophecy": "^1.10",
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
}, },
"type": "library", "type": "library",
@@ -108,7 +184,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/DeepCopy/issues", "issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0"
}, },
"funding": [ "funding": [
{ {
@@ -116,7 +192,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-03-08T13:26:56+00:00" "time": "2024-06-12T14:39:25+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
@@ -296,16 +372,16 @@
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "11.0.3", "version": "11.0.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92" "reference": "4dc2b7a606073f0fb80da09842ffb068b627c38f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e35a2cbcabac0e6865fd373742ea432a3c34f92", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4dc2b7a606073f0fb80da09842ffb068b627c38f",
"reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92", "reference": "4dc2b7a606073f0fb80da09842ffb068b627c38f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -362,7 +438,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.3" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.4"
}, },
"funding": [ "funding": [
{ {
@@ -370,7 +446,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-03-12T15:35:40+00:00" "time": "2024-06-29T08:26:25+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -619,16 +695,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "11.2.1", "version": "11.2.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "1b8775732e9c401bda32df3ffbdf90dec7533ceb" "reference": "be9e3ed32a1287a9bfda15936cc86fef4e4cf591"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b8775732e9c401bda32df3ffbdf90dec7533ceb", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/be9e3ed32a1287a9bfda15936cc86fef4e4cf591",
"reference": "1b8775732e9c401bda32df3ffbdf90dec7533ceb", "reference": "be9e3ed32a1287a9bfda15936cc86fef4e4cf591",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -652,7 +728,7 @@
"sebastian/comparator": "^6.0", "sebastian/comparator": "^6.0",
"sebastian/diff": "^6.0", "sebastian/diff": "^6.0",
"sebastian/environment": "^7.0", "sebastian/environment": "^7.0",
"sebastian/exporter": "^6.0", "sebastian/exporter": "^6.1.2",
"sebastian/global-state": "^7.0", "sebastian/global-state": "^7.0",
"sebastian/object-enumerator": "^6.0", "sebastian/object-enumerator": "^6.0",
"sebastian/type": "^5.0", "sebastian/type": "^5.0",
@@ -699,7 +775,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.1" "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.5"
}, },
"funding": [ "funding": [
{ {
@@ -715,7 +791,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-06-11T07:30:35+00:00" "time": "2024-06-20T13:11:31+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -1155,16 +1231,16 @@
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "6.0.1", "version": "6.1.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da" "reference": "507d2333cbc4e6ea248fbda2d45ee1511e03da13"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/f291e5a317c321c0381fa9ecc796fa2d21b186da", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/507d2333cbc4e6ea248fbda2d45ee1511e03da13",
"reference": "f291e5a317c321c0381fa9ecc796fa2d21b186da", "reference": "507d2333cbc4e6ea248fbda2d45ee1511e03da13",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1173,12 +1249,12 @@
"sebastian/recursion-context": "^6.0" "sebastian/recursion-context": "^6.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11.0" "phpunit/phpunit": "^11.2"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "6.0-dev" "dev-main": "6.1-dev"
} }
}, },
"autoload": { "autoload": {
@@ -1221,7 +1297,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues", "issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy", "security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/6.0.1" "source": "https://github.com/sebastianbergmann/exporter/tree/6.1.2"
}, },
"funding": [ "funding": [
{ {
@@ -1229,7 +1305,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-03-02T07:28:20+00:00" "time": "2024-06-18T11:19:56+00:00"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@@ -1467,16 +1543,16 @@
}, },
{ {
"name": "sebastian/recursion-context", "name": "sebastian/recursion-context",
"version": "6.0.0", "version": "6.0.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git", "url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4" "reference": "2f15508e17af4ea35129bbc32ce28a814d9c7426"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b75224967b5a466925c6d54e68edd0edf8dd4ed4", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2f15508e17af4ea35129bbc32ce28a814d9c7426",
"reference": "b75224967b5a466925c6d54e68edd0edf8dd4ed4", "reference": "2f15508e17af4ea35129bbc32ce28a814d9c7426",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1519,7 +1595,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues", "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"security": "https://github.com/sebastianbergmann/recursion-context/security/policy", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.0" "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.1"
}, },
"funding": [ "funding": [
{ {
@@ -1527,7 +1603,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-02-02T06:08:48+00:00" "time": "2024-06-17T05:22:57+00:00"
}, },
{ {
"name": "sebastian/type", "name": "sebastian/type",
@@ -1640,6 +1716,54 @@
], ],
"time": "2024-02-02T06:10:47+00:00" "time": "2024-02-02T06:10:47+00:00"
}, },
{
"name": "square/pjson",
"version": "v0.5.0",
"source": {
"type": "git",
"url": "https://github.com/square/pjson.git",
"reference": "cf9f9a7810ad7287b30658f60c0bbbba80217319"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/square/pjson/zipball/cf9f9a7810ad7287b30658f60c0bbbba80217319",
"reference": "cf9f9a7810ad7287b30658f60c0bbbba80217319",
"shasum": ""
},
"require": {
"php": ">=8.0"
},
"require-dev": {
"orchestra/testbench": "^7.11",
"phpstan/phpstan": "^1.8",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.7",
"symfony/var-dumper": "^6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Square\\Pjson\\": "src/",
"Square\\Pjson\\Tests\\": "tests/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Seb",
"email": "sebastien@squareup.com"
}
],
"description": "Library for JSON <=> PHP model serialization / deserialization. Deserialize JSON directly into your object model PHP classes.",
"support": {
"issues": "https://github.com/square/pjson/issues",
"source": "https://github.com/square/pjson/tree/v0.5.0"
},
"time": "2024-03-15T18:19:22+00:00"
},
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
"version": "1.2.3", "version": "1.2.3",

View File

@@ -6,6 +6,7 @@ use BitBadger\PDODocument\Mapper\Mapper;
use PDO; use PDO;
use PDOException; use PDOException;
use PDOStatement; use PDOStatement;
use PhpOption\{None, Option, Some};
/** /**
* Functions to execute custom queries * Functions to execute custom queries
@@ -92,14 +93,14 @@ class Custom
* @param string $query The query to be executed (will have "LIMIT 1" appended) * @param string $query The query to be executed (will have "LIMIT 1" appended)
* @param array $parameters Parameters to use in executing the query * @param array $parameters Parameters to use in executing the query
* @param Mapper<TDoc> $mapper Mapper to deserialize the result * @param Mapper<TDoc> $mapper Mapper to deserialize the result
* @return false|TDoc The item if it is found, false if not * @return Option<TDoc> A `Some` instance if the item is found, `None` otherwise
* @throws DocumentException If any is encountered * @throws DocumentException If any is encountered
*/ */
public static function single(string $query, array $parameters, Mapper $mapper): mixed public static function single(string $query, array $parameters, Mapper $mapper): Option
{ {
try { try {
$stmt = &self::runQuery("$query LIMIT 1", $parameters); $stmt = &self::runQuery("$query LIMIT 1", $parameters);
return ($first = $stmt->fetch(PDO::FETCH_ASSOC)) ? $mapper->map($first) : false; return ($first = $stmt->fetch(PDO::FETCH_ASSOC)) ? Some::create($mapper->map($first)) : None::create();
} finally { } finally {
$stmt = null; $stmt = null;
} }

View File

@@ -3,6 +3,7 @@
namespace BitBadger\PDODocument; namespace BitBadger\PDODocument;
use BitBadger\PDODocument\Mapper\DocumentMapper; use BitBadger\PDODocument\Mapper\DocumentMapper;
use PhpOption\Option;
/** /**
* Functions to find documents * Functions to find documents
@@ -30,10 +31,10 @@ class Find
* @param string $tableName The table from which the document should be retrieved * @param string $tableName The table from which the document should be retrieved
* @param mixed $docId The ID of the document to retrieve * @param mixed $docId The ID of the document to retrieve
* @param class-string<TDoc> $className The name of the class to be retrieved * @param class-string<TDoc> $className The name of the class to be retrieved
* @return false|TDoc The document if it exists, false if not * @return Option<TDoc> A `Some` instance if the document is found, `None` otherwise
* @throws DocumentException If any is encountered * @throws DocumentException If any is encountered
*/ */
public static function byId(string $tableName, mixed $docId, string $className): mixed public static function byId(string $tableName, mixed $docId, string $className): Option
{ {
return Custom::single(Query\Find::byId($tableName, $docId), Parameters::id($docId), return Custom::single(Query\Find::byId($tableName, $docId), Parameters::id($docId),
new DocumentMapper($className)); new DocumentMapper($className));
@@ -97,11 +98,11 @@ class Find
* @param array|Field[] $fields The field comparison to match * @param array|Field[] $fields The field comparison to match
* @param class-string<TDoc> $className The name of the class to be retrieved * @param class-string<TDoc> $className The name of the class to be retrieved
* @param FieldMatch|null $match How to handle multiple conditions (optional; defaults to All) * @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 * @return Option<TDoc> A `Some` instance with the first document if any matches are found, `None` otherwise
* @throws DocumentException If any is encountered * @throws DocumentException If any is encountered
*/ */
public static function firstByFields(string $tableName, array $fields, string $className, public static function firstByFields(string $tableName, array $fields, string $className,
?FieldMatch $match = null): mixed ?FieldMatch $match = null): Option
{ {
$namedFields = Parameters::nameFields($fields); $namedFields = Parameters::nameFields($fields);
return Custom::single(Query\Find::byFields($tableName, $namedFields, $match), return Custom::single(Query\Find::byFields($tableName, $namedFields, $match),
@@ -115,10 +116,10 @@ class Find
* @param string $tableName The name of the table from which documents should be retrieved * @param string $tableName The name of the table from which documents should be retrieved
* @param array|object $criteria The criteria for the JSON containment query * @param array|object $criteria The criteria for the JSON containment query
* @param class-string<TDoc> $className The name of the class to be retrieved * @param class-string<TDoc> $className The name of the class to be retrieved
* @return false|TDoc The first document matching the JSON containment query if any is found, false otherwise * @return Option<TDoc> A `Some` instance with the first document if any matches are found, `None` otherwise
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs * @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
*/ */
public static function firstByContains(string $tableName, array|object $criteria, string $className): mixed public static function firstByContains(string $tableName, array|object $criteria, string $className): Option
{ {
return Custom::single(Query\Find::byContains($tableName), Parameters::json(':criteria', $criteria), return Custom::single(Query\Find::byContains($tableName), Parameters::json(':criteria', $criteria),
new DocumentMapper($className)); new DocumentMapper($className));
@@ -131,10 +132,10 @@ class Find
* @param string $tableName The name of the table from which documents should be retrieved * @param string $tableName The name of the table from which documents should be retrieved
* @param string $path The JSON Path match string * @param string $path The JSON Path match string
* @param class-string<TDoc> $className The name of the class to be retrieved * @param class-string<TDoc> $className The name of the class to be retrieved
* @return false|TDoc The first document matching the JSON Path if any is found, false otherwise * @return Option<TDoc> A `Some` instance with the first document if any matches are found, `None` otherwise
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs * @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
*/ */
public static function firstByJsonPath(string $tableName, string $path, string $className): mixed public static function firstByJsonPath(string $tableName, string $path, string $className): Option
{ {
return Custom::single(Query\Find::byJsonPath($tableName), [':path' => $path], new DocumentMapper($className)); return Custom::single(Query\Find::byJsonPath($tableName), [':path' => $path], new DocumentMapper($className));
} }

View File

@@ -3,8 +3,8 @@
namespace BitBadger\PDODocument\Mapper; namespace BitBadger\PDODocument\Mapper;
use BitBadger\PDODocument\DocumentException; use BitBadger\PDODocument\DocumentException;
use Exception;
use JsonMapper; use JsonMapper;
use JsonMapper_Exception;
/** /**
* Map domain class instances from JSON documents * Map domain class instances from JSON documents
@@ -32,12 +32,15 @@ class DocumentMapper implements Mapper
public function map(array $result): mixed public function map(array $result): mixed
{ {
try { try {
if (method_exists($this->className, 'fromJsonString')) {
return $this->className::fromJsonString($result[$this->fieldName]);
}
$json = json_decode($result[$this->fieldName]); $json = json_decode($result[$this->fieldName]);
if (is_null($json)) { if (is_null($json)) {
throw new DocumentException("Could not map document for $this->className: " . json_last_error_msg()); throw new DocumentException("Could not map document for $this->className: " . json_last_error_msg());
} }
return (new JsonMapper())->map($json, $this->className); return (new JsonMapper())->map($json, $this->className);
} catch (JsonMapper_Exception $ex) { } catch (Exception $ex) {
throw new DocumentException("Could not map document for $this->className", previous: $ex); throw new DocumentException("Could not map document for $this->className", previous: $ex);
} }
} }

View File

@@ -27,7 +27,10 @@ class Parameters
*/ */
public static function json(string $name, object|array $document): array public static function json(string $name, object|array $document): array
{ {
return [$name => json_encode($document, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)]; return [$name => match (is_object($document) && method_exists($document, 'toJson')) {
true => $document->toJson(),
false => json_encode($document, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
}];
} }
/** /**

16
tests/PjsonDocument.php Normal file
View File

@@ -0,0 +1,16 @@
<?php declare(strict_types=1);
namespace Test;
use Square\Pjson\{Json, JsonSerialize};
/**
* A test document annotated with pjson attributes using the `JsonSerialize` trait
*/
class PjsonDocument
{
use JsonSerialize;
public function __construct(#[Json] public PjsonId $id = new PjsonId(''), #[Json] public string $name = '',
#[Json('num_value')] public int $numValue = 0, public string $skipped = 'yep') { }
}

23
tests/PjsonId.php Normal file
View File

@@ -0,0 +1,23 @@
<?php declare(strict_types=1);
namespace Test;
use Square\Pjson\JsonDataSerializable;
/**
* A serializable ID wrapper class
*/
class PjsonId implements JsonDataSerializable
{
public function __construct(protected string $value) { }
public function toJsonData(): string
{
return $this->value;
}
public static function fromJsonData($jd, array|string $path = []): static
{
return new static($jd);
}
}

View File

@@ -87,15 +87,15 @@ class CustomTest extends TestCase
{ {
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", [':id' => 'one'], $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", [':id' => 'one'],
new DocumentMapper(TestDocument::class)); new DocumentMapper(TestDocument::class));
$this->assertNotNull($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('one', $doc->id, 'The incorrect document was returned'); $this->assertEquals('one', $doc->get()->id, 'The incorrect document was returned');
} }
public function testSingleSucceedsWhenARowIsNotFound(): void public function testSingleSucceedsWhenARowIsNotFound(): void
{ {
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id",
[':id' => 'eighty'], new DocumentMapper(TestDocument::class)); [':id' => 'eighty'], new DocumentMapper(TestDocument::class));
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testNonQuerySucceedsWhenOperatingOnData() public function testNonQuerySucceedsWhenOperatingOnData()

View File

@@ -33,8 +33,9 @@ class DocumentTest extends TestCase
public function testInsertSucceedsForArrayNoAutoId(): void public function testInsertSucceedsForArrayNoAutoId(): void
{ {
Document::insert(ThrowawayDb::TABLE, ['id' => 'turkey', 'sub' => ['foo' => 'gobble', 'bar' => 'gobble']]); Document::insert(ThrowawayDb::TABLE, ['id' => 'turkey', 'sub' => ['foo' => 'gobble', 'bar' => 'gobble']]);
$doc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document inserted'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document inserted');
$doc = $tryDoc->get();
$this->assertEquals('turkey', $doc->id, 'The ID was incorrect'); $this->assertEquals('turkey', $doc->id, 'The ID was incorrect');
$this->assertEquals('', $doc->value, 'The value was incorrect'); $this->assertEquals('', $doc->value, 'The value was incorrect');
$this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect');
@@ -52,15 +53,15 @@ class DocumentTest extends TestCase
Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'new', 'num_value' => 8]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper()); $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(1, $obj->id, 'The ID 1 should have been auto-generated'); $this->assertEquals(1, $obj->id, 'The ID 1 should have been auto-generated');
Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'again', 'num_value' => 7]); Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'again', 'num_value' => 7]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE " . Query::whereById(docId: 2), $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE " . Query::whereById(docId: 2),
[':id' => 2], new ArrayMapper()); [':id' => 2], new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(2, $obj->id, 'The ID 2 should have been auto-generated'); $this->assertEquals(2, $obj->id, 'The ID 2 should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
@@ -75,8 +76,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => 7, 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => 7, 'value' => 'new', 'num_value' => 8]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper()); $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(7, $obj->id, 'The ID 7 should have been stored'); $this->assertEquals(7, $obj->id, 'The ID 7 should have been stored');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
@@ -91,8 +92,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => '', 'num_value' => 5]); Document::insert(ThrowawayDb::TABLE, ['id' => '', 'num_value' => 5]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 5)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 5)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNotEmpty($doc->id, 'The ID should have been auto-generated'); $this->assertNotEmpty($doc->get()->id, 'The ID should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -107,8 +108,8 @@ class DocumentTest extends TestCase
$uuid = AutoId::generateUUID(); $uuid = AutoId::generateUUID();
Document::insert(ThrowawayDb::TABLE, ['id' => $uuid, 'value' => 'uuid', 'num_value' => 12]); Document::insert(ThrowawayDb::TABLE, ['id' => $uuid, 'value' => 'uuid', 'num_value' => 12]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 12)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 12)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals($uuid, $doc->id, 'The ID should not have been changed'); $this->assertEquals($uuid, $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -123,8 +124,9 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => '', 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => '', 'value' => 'new', 'num_value' => 8]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 8)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 8)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(6, strlen($doc->id), 'The ID should have been auto-generated and had 6 characters'); $this->assertEquals(6, strlen($doc->get()->id),
'The ID should have been auto-generated and had 6 characters');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
Configuration::$idStringLength = 16; Configuration::$idStringLength = 16;
@@ -139,8 +141,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => 'my-key', 'value' => 'old', 'num_value' => 3]); Document::insert(ThrowawayDb::TABLE, ['id' => 'my-key', 'value' => 'old', 'num_value' => 3]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('my-key', $doc->id, 'The ID should not have been changed'); $this->assertEquals('my-key', $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -150,8 +152,9 @@ class DocumentTest extends TestCase
public function testInsertSucceedsForObjectNoAutoId(): void public function testInsertSucceedsForObjectNoAutoId(): void
{ {
Document::insert(ThrowawayDb::TABLE, new TestDocument('turkey', sub: new SubDocument('gobble', 'gobble'))); Document::insert(ThrowawayDb::TABLE, new TestDocument('turkey', sub: new SubDocument('gobble', 'gobble')));
$doc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document inserted'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document inserted');
$doc = $tryDoc->get();
$this->assertEquals('turkey', $doc->id, 'The ID was incorrect'); $this->assertEquals('turkey', $doc->id, 'The ID was incorrect');
$this->assertEquals('', $doc->value, 'The value was incorrect'); $this->assertEquals('', $doc->value, 'The value was incorrect');
$this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect');
@@ -169,13 +172,13 @@ class DocumentTest extends TestCase
Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'taco')); Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'taco'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'taco')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'taco')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(1, $doc->id, 'The ID 1 should have been auto-generated'); $this->assertEquals(1, $doc->get()->id, 'The ID 1 should have been auto-generated');
Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'burrito')); Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'burrito'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'burrito')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'burrito')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(2, $doc->id, 'The ID 2 should have been auto-generated'); $this->assertEquals(2, $doc->get()->id, 'The ID 2 should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -189,8 +192,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new NumDocument(64, 'large')); Document::insert(ThrowawayDb::TABLE, new NumDocument(64, 'large'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'large')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'large')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(64, $doc->id, 'The ID 64 should have been stored'); $this->assertEquals(64, $doc->get()->id, 'The ID 64 should have been stored');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -204,8 +207,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument(value: 'something', num_value: 9)); Document::insert(ThrowawayDb::TABLE, new TestDocument(value: 'something', num_value: 9));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EX('value')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EX('value')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNotEmpty($doc->id, 'The ID should have been auto-generated'); $this->assertNotEmpty($doc->get()->id, 'The ID should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -220,8 +223,8 @@ class DocumentTest extends TestCase
$uuid = AutoId::generateUUID(); $uuid = AutoId::generateUUID();
Document::insert(ThrowawayDb::TABLE, new TestDocument($uuid, num_value: 14)); Document::insert(ThrowawayDb::TABLE, new TestDocument($uuid, num_value: 14));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 14)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 14)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals($uuid, $doc->id, 'The ID should not have been changed'); $this->assertEquals($uuid, $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -236,8 +239,9 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument(num_value: 55)); Document::insert(ThrowawayDb::TABLE, new TestDocument(num_value: 55));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 55)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 55)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(40, strlen($doc->id), 'The ID should have been auto-generated and had 40 characters'); $this->assertEquals(40, strlen($doc->get()->id),
'The ID should have been auto-generated and had 40 characters');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
Configuration::$idStringLength = 16; Configuration::$idStringLength = 16;
@@ -252,8 +256,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument('my-key', num_value: 3)); Document::insert(ThrowawayDb::TABLE, new TestDocument('my-key', num_value: 3));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('my-key', $doc->id, 'The ID should not have been changed'); $this->assertEquals('my-key', $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -269,14 +273,15 @@ class DocumentTest extends TestCase
{ {
Document::save(ThrowawayDb::TABLE, new TestDocument('test', sub: new SubDocument('a', 'b'))); Document::save(ThrowawayDb::TABLE, new TestDocument('test', sub: new SubDocument('a', 'b')));
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
} }
public function testSaveSucceedsWhenADocumentIsUpdated(): void public function testSaveSucceedsWhenADocumentIsUpdated(): void
{ {
Document::save(ThrowawayDb::TABLE, new TestDocument('two', num_value: 44)); Document::save(ThrowawayDb::TABLE, new TestDocument('two', num_value: 44));
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals(44, $doc->num_value, 'The numeric value was not updated'); $this->assertEquals(44, $doc->num_value, 'The numeric value was not updated');
$this->assertNull($doc->sub, 'The sub-document should have been null'); $this->assertNull($doc->sub, 'The sub-document should have been null');
} }
@@ -284,8 +289,9 @@ class DocumentTest extends TestCase
public function testUpdateSucceedsWhenReplacingADocument(): void public function testUpdateSucceedsWhenReplacingADocument(): void
{ {
Document::update(ThrowawayDb::TABLE, 'one', new TestDocument('one', 'howdy', 8, new SubDocument('y', 'z'))); Document::update(ThrowawayDb::TABLE, 'one', new TestDocument('one', 'howdy', 8, new SubDocument('y', 'z')));
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertNotFalse($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals('howdy', $doc->value, 'The value was incorrect'); $this->assertEquals('howdy', $doc->value, 'The value was incorrect');
$this->assertEquals(8, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(8, $doc->num_value, 'The numeric value was incorrect');
$this->assertNotNull($doc->sub, 'The sub-document should not have been null'); $this->assertNotNull($doc->sub, 'The sub-document should not have been null');
@@ -297,6 +303,6 @@ class DocumentTest extends TestCase
{ {
Document::update(ThrowawayDb::TABLE, 'two-hundred', new TestDocument('200')); Document::update(ThrowawayDb::TABLE, 'two-hundred', new TestDocument('200'));
$doc = Find::byId(ThrowawayDb::TABLE, 'two-hundred', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'two-hundred', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
} }

View File

@@ -49,8 +49,8 @@ class FindTest extends TestCase
public function testByIdSucceedsWhenADocumentIsFound(): void public function testByIdSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'An incorrect document was returned'); $this->assertEquals('two', $doc->get()->id, 'An incorrect document was returned');
} }
#[TestDox('By ID succeeds when a document is found with numeric ID')] #[TestDox('By ID succeeds when a document is found with numeric ID')]
@@ -59,15 +59,15 @@ class FindTest extends TestCase
Delete::byFields(ThrowawayDb::TABLE, [Field::NEX('absent')]); Delete::byFields(ThrowawayDb::TABLE, [Field::NEX('absent')]);
Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']);
$doc = Find::byId(ThrowawayDb::TABLE, 18, NumDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 18, NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(18, $doc->id, 'An incorrect document was returned'); $this->assertEquals(18, $doc->get()->id, 'An incorrect document was returned');
} }
#[TestDox('By ID succeeds when a document is not found')] #[TestDox('By ID succeeds when a document is not found')]
public function testByIdSucceedsWhenADocumentIsNotFound(): void public function testByIdSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testByFieldsSucceedsWhenDocumentsAreFound(): void public function testByFieldsSucceedsWhenDocumentsAreFound(): void
@@ -123,62 +123,63 @@ class FindTest extends TestCase
public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'another')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'another')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'The incorrect document was returned'); $this->assertEquals('two', $doc->get()->id, 'The incorrect document was returned');
} }
public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('sub.foo', 'green')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('sub.foo', 'green')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertContains($doc->id, ['two', 'four'], 'An incorrect document was returned'); $this->assertContains($doc->get()->id, ['two', 'four'], 'An incorrect document was returned');
} }
public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'absent')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'absent')], TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testFirstByContainsSucceedsWhenADocumentIsFound(): void public function testFirstByContainsSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'], TestDocument::class); $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'FIRST!'], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('one', $doc->id, 'The incorrect document was returned'); $this->assertEquals('one', $doc->get()->id, 'The incorrect document was returned');
} }
public function testFirstByContainsSucceedsWhenMultipleDocumentsAreFound(): void public function testFirstByContainsSucceedsWhenMultipleDocumentsAreFound(): void
{ {
$doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class); $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'purple'], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertContains($doc->id, ['four', 'five'], 'An incorrect document was returned'); $this->assertContains($doc->get()->id, ['four', 'five'], 'An incorrect document was returned');
} }
public function testFirstByContainsSucceedsWhenADocumentIsNotFound(): void public function testFirstByContainsSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class); $doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'indigo'], TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
#[TestDox('First by JSON Path succeeds when a document is found')] #[TestDox('First by JSON Path succeeds when a document is found')]
public function testFirstByJsonPathSucceedsWhenADocumentIsFound(): void public function testFirstByJsonPathSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)', TestDocument::class); $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ == 10)', TestDocument::class);
$this->assertEquals('two', $doc->id, 'The incorrect document was returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('two', $doc->get()->id, 'The incorrect document was returned');
} }
#[TestDox('First by JSON Path succeeds when multiple documents are found')] #[TestDox('First by JSON Path succeeds when multiple documents are found')]
public function testFirstByJsonPathSucceedsWhenMultipleDocumentsAreFound(): void public function testFirstByJsonPathSucceedsWhenMultipleDocumentsAreFound(): void
{ {
$doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class); $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 10)', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertContains($doc->id, ['four', 'five'], 'An incorrect document was returned'); $this->assertContains($doc->get()->id, ['four', 'five'], 'An incorrect document was returned');
} }
#[TestDox('First by JSON Path succeeds when a document is not found')] #[TestDox('First by JSON Path succeeds when a document is not found')]
public function testFirstByJsonPathSucceedsWhenADocumentIsNotFound(): void public function testFirstByJsonPathSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class); $doc = Find::firstByJsonPath(ThrowawayDb::TABLE, '$.num_value ? (@ > 100)', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
} }

View File

@@ -33,8 +33,8 @@ class PatchTest extends TestCase
{ {
Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]); Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]);
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(44, $doc->num_value, 'The updated document is not correct'); $this->assertEquals(44, $doc->get()->num_value, 'The updated document is not correct');
} }
#[TestDox('By ID succeeds when no document is updated')] #[TestDox('By ID succeeds when no document is updated')]
@@ -64,8 +64,9 @@ class PatchTest extends TestCase
public function testByContainsSucceedsWhenDocumentsAreUpdated(): void public function testByContainsSucceedsWhenDocumentsAreUpdated(): void
{ {
Patch::byContains(ThrowawayDb::TABLE, ['value' => 'another'], ['num_value' => 12]); Patch::byContains(ThrowawayDb::TABLE, ['value' => 'another'], ['num_value' => 12]);
$doc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'another'], TestDocument::class); $tryDoc = Find::firstByContains(ThrowawayDb::TABLE, ['value' => 'another'], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertNotFalse($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals('two', $doc->id, 'An incorrect document was returned'); $this->assertEquals('two', $doc->id, 'An incorrect document was returned');
$this->assertEquals(12, $doc->num_value, 'The document was not patched'); $this->assertEquals(12, $doc->num_value, 'The document was not patched');
} }

View File

@@ -32,8 +32,9 @@ class RemoveFieldsTest extends TestCase
public function testByIdSucceedsWhenFieldsAreRemoved(): void public function testByIdSucceedsWhenFieldsAreRemoved(): void
{ {
RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']); RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']);
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertNotFalse($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals('', $doc->value, 'Value should have been blank (its default value)'); $this->assertEquals('', $doc->value, 'Value should have been blank (its default value)');
$this->assertNull($doc->sub, 'Sub-document should have been null'); $this->assertNull($doc->sub, 'Sub-document should have been null');
} }
@@ -56,8 +57,8 @@ class RemoveFieldsTest extends TestCase
{ {
RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['sub']); RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['sub']);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNull($doc->sub, 'Sub-document should have been null'); $this->assertNull($doc->get()->sub, 'Sub-document should have been null');
} }
public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void

View File

@@ -86,15 +86,15 @@ class CustomTest extends TestCase
{ {
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", [':id' => 'one'], $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", [':id' => 'one'],
new DocumentMapper(TestDocument::class)); new DocumentMapper(TestDocument::class));
$this->assertNotNull($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('one', $doc->id, 'The incorrect document was returned'); $this->assertEquals('one', $doc->get()->id, 'The incorrect document was returned');
} }
public function testSingleSucceedsWhenARowIsNotFound(): void public function testSingleSucceedsWhenARowIsNotFound(): void
{ {
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id", $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = :id",
[':id' => 'eighty'], new DocumentMapper(TestDocument::class)); [':id' => 'eighty'], new DocumentMapper(TestDocument::class));
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testNonQuerySucceedsWhenOperatingOnData() public function testNonQuerySucceedsWhenOperatingOnData()

View File

@@ -33,8 +33,9 @@ class DocumentTest extends TestCase
public function testInsertSucceedsForArrayNoAutoId(): void public function testInsertSucceedsForArrayNoAutoId(): void
{ {
Document::insert(ThrowawayDb::TABLE, ['id' => 'turkey', 'sub' => ['foo' => 'gobble', 'bar' => 'gobble']]); Document::insert(ThrowawayDb::TABLE, ['id' => 'turkey', 'sub' => ['foo' => 'gobble', 'bar' => 'gobble']]);
$doc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document inserted'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document inserted');
$doc = $tryDoc->get();
$this->assertEquals('turkey', $doc->id, 'The ID was incorrect'); $this->assertEquals('turkey', $doc->id, 'The ID was incorrect');
$this->assertEquals('', $doc->value, 'The value was incorrect'); $this->assertEquals('', $doc->value, 'The value was incorrect');
$this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect');
@@ -52,15 +53,15 @@ class DocumentTest extends TestCase
Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'new', 'num_value' => 8]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper()); $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(1, $obj->id, 'The ID 1 should have been auto-generated'); $this->assertEquals(1, $obj->id, 'The ID 1 should have been auto-generated');
Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'again', 'num_value' => 7]); Document::insert(ThrowawayDb::TABLE, ['id' => 0, 'value' => 'again', 'num_value' => 7]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = 2", [], $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE . " WHERE data->>'id' = 2", [],
new ArrayMapper()); new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(2, $obj->id, 'The ID 2 should have been auto-generated'); $this->assertEquals(2, $obj->id, 'The ID 2 should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
@@ -75,8 +76,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => 7, 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => 7, 'value' => 'new', 'num_value' => 8]);
$doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper()); $doc = Custom::single('SELECT data FROM ' . ThrowawayDb::TABLE, [], new ArrayMapper());
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$obj = json_decode($doc['data']); $obj = json_decode($doc->get()['data']);
$this->assertEquals(7, $obj->id, 'The ID 7 should have been stored'); $this->assertEquals(7, $obj->id, 'The ID 7 should have been stored');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
@@ -91,8 +92,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => '', 'num_value' => 5]); Document::insert(ThrowawayDb::TABLE, ['id' => '', 'num_value' => 5]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 5)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 5)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNotEmpty($doc->id, 'The ID should have been auto-generated'); $this->assertNotEmpty($doc->get()->id, 'The ID should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -107,8 +108,8 @@ class DocumentTest extends TestCase
$uuid = AutoId::generateUUID(); $uuid = AutoId::generateUUID();
Document::insert(ThrowawayDb::TABLE, ['id' => $uuid, 'value' => 'uuid', 'num_value' => 12]); Document::insert(ThrowawayDb::TABLE, ['id' => $uuid, 'value' => 'uuid', 'num_value' => 12]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 12)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 12)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals($uuid, $doc->id, 'The ID should not have been changed'); $this->assertEquals($uuid, $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -123,8 +124,9 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => '', 'value' => 'new', 'num_value' => 8]); Document::insert(ThrowawayDb::TABLE, ['id' => '', 'value' => 'new', 'num_value' => 8]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 8)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 8)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(6, strlen($doc->id), 'The ID should have been auto-generated and had 6 characters'); $this->assertEquals(6, strlen($doc->get()->id),
'The ID should have been auto-generated and had 6 characters');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
Configuration::$idStringLength = 16; Configuration::$idStringLength = 16;
@@ -139,8 +141,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, ['id' => 'my-key', 'value' => 'old', 'num_value' => 3]); Document::insert(ThrowawayDb::TABLE, ['id' => 'my-key', 'value' => 'old', 'num_value' => 3]);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('my-key', $doc->id, 'The ID should not have been changed'); $this->assertEquals('my-key', $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -150,8 +152,9 @@ class DocumentTest extends TestCase
public function testInsertSucceedsForObjectNoAutoId(): void public function testInsertSucceedsForObjectNoAutoId(): void
{ {
Document::insert(ThrowawayDb::TABLE, new TestDocument('turkey', sub: new SubDocument('gobble', 'gobble'))); Document::insert(ThrowawayDb::TABLE, new TestDocument('turkey', sub: new SubDocument('gobble', 'gobble')));
$doc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'turkey', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document inserted'); $this->assertNotFalse($tryDoc->isDefined(), 'There should have been a document inserted');
$doc = $tryDoc->get();
$this->assertEquals('turkey', $doc->id, 'The ID was incorrect'); $this->assertEquals('turkey', $doc->id, 'The ID was incorrect');
$this->assertEquals('', $doc->value, 'The value was incorrect'); $this->assertEquals('', $doc->value, 'The value was incorrect');
$this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(0, $doc->num_value, 'The numeric value was incorrect');
@@ -169,13 +172,13 @@ class DocumentTest extends TestCase
Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'taco')); Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'taco'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'taco')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'taco')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(1, $doc->id, 'The ID 1 should have been auto-generated'); $this->assertEquals(1, $doc->get()->id, 'The ID 1 should have been auto-generated');
Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'burrito')); Document::insert(ThrowawayDb::TABLE, new NumDocument(value: 'burrito'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'burrito')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'burrito')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(2, $doc->id, 'The ID 2 should have been auto-generated'); $this->assertEquals(2, $doc->get()->id, 'The ID 2 should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -189,8 +192,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new NumDocument(64, 'large')); Document::insert(ThrowawayDb::TABLE, new NumDocument(64, 'large'));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'large')], NumDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'large')], NumDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(64, $doc->id, 'The ID 64 should have been stored'); $this->assertEquals(64, $doc->get()->id, 'The ID 64 should have been stored');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -204,8 +207,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument(value: 'something', num_value: 9)); Document::insert(ThrowawayDb::TABLE, new TestDocument(value: 'something', num_value: 9));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EX('value')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EX('value')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNotEmpty($doc->id, 'The ID should have been auto-generated'); $this->assertNotEmpty($doc->get()->id, 'The ID should have been auto-generated');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -220,8 +223,8 @@ class DocumentTest extends TestCase
$uuid = AutoId::generateUUID(); $uuid = AutoId::generateUUID();
Document::insert(ThrowawayDb::TABLE, new TestDocument($uuid, num_value: 14)); Document::insert(ThrowawayDb::TABLE, new TestDocument($uuid, num_value: 14));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 14)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 14)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals($uuid, $doc->id, 'The ID should not have been changed'); $this->assertEquals($uuid, $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -236,8 +239,9 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument(num_value: 55)); Document::insert(ThrowawayDb::TABLE, new TestDocument(num_value: 55));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 55)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 55)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(40, strlen($doc->id), 'The ID should have been auto-generated and had 40 characters'); $this->assertEquals(40, strlen($doc->get()->id),
'The ID should have been auto-generated and had 40 characters');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
Configuration::$idStringLength = 16; Configuration::$idStringLength = 16;
@@ -252,8 +256,8 @@ class DocumentTest extends TestCase
Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []); Custom::nonQuery('DELETE FROM ' . ThrowawayDb::TABLE, []);
Document::insert(ThrowawayDb::TABLE, new TestDocument('my-key', num_value: 3)); Document::insert(ThrowawayDb::TABLE, new TestDocument('my-key', num_value: 3));
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 3)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('my-key', $doc->id, 'The ID should not have been changed'); $this->assertEquals('my-key', $doc->get()->id, 'The ID should not have been changed');
} finally { } finally {
Configuration::$autoId = AutoId::None; Configuration::$autoId = AutoId::None;
} }
@@ -269,14 +273,15 @@ class DocumentTest extends TestCase
{ {
Document::save(ThrowawayDb::TABLE, new TestDocument('test', sub: new SubDocument('a', 'b'))); Document::save(ThrowawayDb::TABLE, new TestDocument('test', sub: new SubDocument('a', 'b')));
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
} }
public function testSaveSucceedsWhenADocumentIsUpdated(): void public function testSaveSucceedsWhenADocumentIsUpdated(): void
{ {
Document::save(ThrowawayDb::TABLE, new TestDocument('two', num_value: 44)); Document::save(ThrowawayDb::TABLE, new TestDocument('two', num_value: 44));
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals(44, $doc->num_value, 'The numeric value was not updated'); $this->assertEquals(44, $doc->num_value, 'The numeric value was not updated');
$this->assertNull($doc->sub, 'The sub-document should have been null'); $this->assertNull($doc->sub, 'The sub-document should have been null');
} }
@@ -284,8 +289,9 @@ class DocumentTest extends TestCase
public function testUpdateSucceedsWhenReplacingADocument(): void public function testUpdateSucceedsWhenReplacingADocument(): void
{ {
Document::update(ThrowawayDb::TABLE, 'one', new TestDocument('one', 'howdy', 8, new SubDocument('y', 'z'))); Document::update(ThrowawayDb::TABLE, 'one', new TestDocument('one', 'howdy', 8, new SubDocument('y', 'z')));
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals('howdy', $doc->value, 'The value was incorrect'); $this->assertEquals('howdy', $doc->value, 'The value was incorrect');
$this->assertEquals(8, $doc->num_value, 'The numeric value was incorrect'); $this->assertEquals(8, $doc->num_value, 'The numeric value was incorrect');
$this->assertNotNull($doc->sub, 'The sub-document should not have been null'); $this->assertNotNull($doc->sub, 'The sub-document should not have been null');
@@ -297,6 +303,6 @@ class DocumentTest extends TestCase
{ {
Document::update(ThrowawayDb::TABLE, 'two-hundred', new TestDocument('200')); Document::update(ThrowawayDb::TABLE, 'two-hundred', new TestDocument('200'));
$doc = Find::byId(ThrowawayDb::TABLE, 'two-hundred', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'two-hundred', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
} }

View File

@@ -49,8 +49,8 @@ class FindTest extends TestCase
public function testByIdSucceedsWhenADocumentIsFound(): void public function testByIdSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'An incorrect document was returned'); $this->assertEquals('two', $doc->get()->id, 'An incorrect document was returned');
} }
#[TestDox('By ID succeeds when a document is found with numeric ID')] #[TestDox('By ID succeeds when a document is found with numeric ID')]
@@ -58,15 +58,15 @@ class FindTest extends TestCase
{ {
Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']); Document::insert(ThrowawayDb::TABLE, ['id' => 18, 'value' => 'howdy']);
$doc = Find::byId(ThrowawayDb::TABLE, 18, TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 18, TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('18', $doc->id, 'An incorrect document was returned'); $this->assertEquals('18', $doc->get()->id, 'An incorrect document was returned');
} }
#[TestDox('By ID succeeds when a document is not found')] #[TestDox('By ID succeeds when a document is not found')]
public function testByIdSucceedsWhenADocumentIsNotFound(): void public function testByIdSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'seventy-five', TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testByFieldsSucceedsWhenDocumentsAreFound(): void public function testByFieldsSucceedsWhenDocumentsAreFound(): void
@@ -101,21 +101,21 @@ class FindTest extends TestCase
public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void public function testFirstByFieldsSucceedsWhenADocumentIsFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'another')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'another')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals('two', $doc->id, 'The incorrect document was returned'); $this->assertEquals('two', $doc->get()->id, 'The incorrect document was returned');
} }
public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void public function testFirstByFieldsSucceedsWhenMultipleDocumentsAreFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('sub.foo', 'green')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('sub.foo', 'green')], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertContains($doc->id, ['two', 'four'], 'An incorrect document was returned'); $this->assertContains($doc->get()->id, ['two', 'four'], 'An incorrect document was returned');
} }
public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void public function testFirstByFieldsSucceedsWhenADocumentIsNotFound(): void
{ {
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'absent')], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('value', 'absent')], TestDocument::class);
$this->assertFalse($doc, 'There should not have been a document returned'); $this->assertTrue($doc->isEmpty(), 'There should not have been a document returned');
} }
public function testFirstByContainsFails(): void public function testFirstByContainsFails(): void

View File

@@ -33,8 +33,8 @@ class PatchTest extends TestCase
{ {
Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]); Patch::byId(ThrowawayDb::TABLE, 'one', ['num_value' => 44]);
$doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class); $doc = Find::byId(ThrowawayDb::TABLE, 'one', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertEquals(44, $doc->num_value, 'The updated document is not correct'); $this->assertEquals(44, $doc->get()->num_value, 'The updated document is not correct');
} }
#[TestDox('By ID succeeds when no document is updated')] #[TestDox('By ID succeeds when no document is updated')]

View File

@@ -32,8 +32,9 @@ class RemoveFieldsTest extends TestCase
public function testByIdSucceedsWhenFieldsAreRemoved(): void public function testByIdSucceedsWhenFieldsAreRemoved(): void
{ {
RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']); RemoveFields::byId(ThrowawayDb::TABLE, 'two', ['sub', 'value']);
$doc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class); $tryDoc = Find::byId(ThrowawayDb::TABLE, 'two', TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($tryDoc->isDefined(), 'There should have been a document returned');
$doc = $tryDoc->get();
$this->assertEquals('', $doc->value, 'Value should have been blank (its default value)'); $this->assertEquals('', $doc->value, 'Value should have been blank (its default value)');
$this->assertNull($doc->sub, 'Sub-document should have been null'); $this->assertNull($doc->sub, 'Sub-document should have been null');
} }
@@ -56,8 +57,8 @@ class RemoveFieldsTest extends TestCase
{ {
RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['sub']); RemoveFields::byFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], ['sub']);
$doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], TestDocument::class); $doc = Find::firstByFields(ThrowawayDb::TABLE, [Field::EQ('num_value', 17)], TestDocument::class);
$this->assertNotFalse($doc, 'There should have been a document returned'); $this->assertTrue($doc->isDefined(), 'There should have been a document returned');
$this->assertNull($doc->sub, 'Sub-document should have been null'); $this->assertNull($doc->get()->sub, 'Sub-document should have been null');
} }
public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void public function testByFieldsSucceedsWhenAFieldIsNotRemoved(): void

View File

@@ -6,6 +6,7 @@ use BitBadger\PDODocument\{DocumentException, Field};
use BitBadger\PDODocument\Mapper\DocumentMapper; use BitBadger\PDODocument\Mapper\DocumentMapper;
use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Test\{PjsonDocument, PjsonId};
// ** Test class hierarchy for serialization ** // ** Test class hierarchy for serialization **
@@ -48,10 +49,28 @@ class DocumentMapperTest extends TestCase
$this->assertEquals('tester', $doc->subDoc->name, 'Sub-document name not filled correctly'); $this->assertEquals('tester', $doc->subDoc->name, 'Sub-document name not filled correctly');
} }
#[TestDox('Map succeeds with valid JSON for pjson class')]
public function testMapSucceedsWithValidJSONForPjsonClass(): void
{
$doc = (new DocumentMapper(PjsonDocument::class))->map(['data' => '{"id":"seven","name":"bob","num_value":8}']);
$this->assertNotNull($doc, 'The document should not have been null');
$this->assertEquals(new PjsonId('seven'), $doc->id, 'ID not filled correctly');
$this->assertEquals('bob', $doc->name, 'Name not filled correctly');
$this->assertEquals(8, $doc->numValue, 'Numeric value not filled correctly');
$this->assertFalse(isset($doc->skipped), 'Non-JSON field has not been set');
}
#[TestDox('Map fails with invalid JSON')] #[TestDox('Map fails with invalid JSON')]
public function testMapFailsWithInvalidJSON(): void public function testMapFailsWithInvalidJSON(): void
{ {
$this->expectException(DocumentException::class); $this->expectException(DocumentException::class);
(new DocumentMapper(TestDocument::class))->map(['data' => 'this is not valid']); (new DocumentMapper(TestDocument::class))->map(['data' => 'this is not valid']);
} }
#[TestDox('Map fails with invalid JSON for pjson class')]
public function testMapFailsWithInvalidJSONForPjsonClass(): void
{
$this->expectException(DocumentException::class);
(new DocumentMapper(PjsonDocument::class))->map(['data' => 'not even close']);
}
} }

View File

@@ -5,6 +5,8 @@ namespace Test\Unit;
use BitBadger\PDODocument\{Configuration, DocumentException, Field, Mode, Parameters}; use BitBadger\PDODocument\{Configuration, DocumentException, Field, Mode, Parameters};
use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use stdClass;
use Test\{PjsonDocument, PjsonId};
/** /**
* Unit tests for the Parameters class * Unit tests for the Parameters class
@@ -24,13 +26,31 @@ class ParametersTest extends TestCase
$this->assertEquals([':id' => '7'], Parameters::id(7), 'ID parameter not constructed correctly'); $this->assertEquals([':id' => '7'], Parameters::id(7), 'ID parameter not constructed correctly');
} }
public function testJsonSucceeds(): void public function testJsonSucceedsForArray(): void
{ {
$this->assertEquals([':it' => '{"id":18,"url":"https://www.unittest.com"}'], $this->assertEquals([':it' => '{"id":18,"url":"https://www.unittest.com"}'],
Parameters::json(':it', ['id' => 18, 'url' => 'https://www.unittest.com']), Parameters::json(':it', ['id' => 18, 'url' => 'https://www.unittest.com']),
'JSON parameter not constructed correctly'); 'JSON parameter not constructed correctly');
} }
#[TestDox('json succeeds for stdClass')]
public function testJsonSucceedsForStdClass(): void
{
$obj = new stdClass();
$obj->id = 19;
$obj->url = 'https://testhere.info';
$this->assertEquals([':it' => '{"id":19,"url":"https://testhere.info"}'], Parameters::json(':it', $obj),
'JSON parameter not constructed correctly');
}
public function testJsonSucceedsForPjsonClass(): void
{
$this->assertEquals([':it' => '{"id":"999","name":"a test","num_value":98}'],
Parameters::json(':it', new PjsonDocument(new PjsonId('999'), 'a test', 98, 'nothing')),
'JSON parameter not constructed correctly');
}
public function testNameFieldsSucceeds(): void public function testNameFieldsSucceeds(): void
{ {
$named = Parameters::nameFields([Field::EQ('it', 17), Field::EQ('also', 22, ':also'), Field::EQ('other', 24)]); $named = Parameters::nameFields([Field::EQ('it', 17), Field::EQ('also', 22, ':also'), Field::EQ('other', 24)]);