Add pjson support

This commit is contained in:
Daniel J. Summers 2024-06-29 11:46:16 -04:00
parent 50854275a8
commit 478684621c
8 changed files with 146 additions and 12 deletions

View File

@ -23,7 +23,8 @@
"phpoption/phpoption": "^1.9"
},
"require-dev": {
"phpunit/phpunit": "^11"
"phpunit/phpunit": "^11",
"square/pjson": "^0.5.0"
},
"autoload": {
"psr-4": {
@ -34,6 +35,7 @@
},
"autoload-dev": {
"psr-4": {
"Test\\": "./tests",
"Test\\Unit\\": "./tests/unit",
"Test\\Integration\\": "./tests/integration",
"Test\\Integration\\PostgreSQL\\": "./tests/integration/postgresql",

62
composer.lock generated
View File

@ -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": "71593fca8aa32b0cd963eb52bad1a34e",
"content-hash": "dc897c0ab21d662a65b3818331edd2f2",
"packages": [
{
"name": "netresearch/jsonmapper",
@ -372,16 +372,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "11.0.3",
"version": "11.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92"
"reference": "4dc2b7a606073f0fb80da09842ffb068b627c38f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e35a2cbcabac0e6865fd373742ea432a3c34f92",
"reference": "7e35a2cbcabac0e6865fd373742ea432a3c34f92",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4dc2b7a606073f0fb80da09842ffb068b627c38f",
"reference": "4dc2b7a606073f0fb80da09842ffb068b627c38f",
"shasum": ""
},
"require": {
@ -438,7 +438,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"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": [
{
@ -446,7 +446,7 @@
"type": "github"
}
],
"time": "2024-03-12T15:35:40+00:00"
"time": "2024-06-29T08:26:25+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -1716,6 +1716,54 @@
],
"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",
"version": "1.2.3",

View File

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

View File

@ -27,7 +27,10 @@ class Parameters
*/
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

@ -6,6 +6,7 @@ use BitBadger\PDODocument\{DocumentException, Field};
use BitBadger\PDODocument\Mapper\DocumentMapper;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use Test\{PjsonDocument, PjsonId};
// ** 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');
}
#[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')]
public function testMapFailsWithInvalidJSON(): void
{
$this->expectException(DocumentException::class);
(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 PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
use stdClass;
use Test\{PjsonDocument, PjsonId};
/**
* 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');
}
public function testJsonSucceeds(): void
public function testJsonSucceedsForArray(): void
{
$this->assertEquals([':it' => '{"id":18,"url":"https://www.unittest.com"}'],
Parameters::json(':it', ['id' => 18, 'url' => 'https://www.unittest.com']),
'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
{
$named = Parameters::nameFields([Field::EQ('it', 17), Field::EQ('also', 22, ':also'), Field::EQ('other', 24)]);