Skip to content

Commit 2722e05

Browse files
authored
Merge pull request #61 from nathansalter/bugfix/59-rpde-body-serialize
Add RpdeBody::deserialize() functionality
2 parents 5dbedc3 + 91363fc commit 2722e05

8 files changed

Lines changed: 150 additions & 34 deletions

File tree

src/Concerns/TypeChecker.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,18 @@ private static function getTypeCheckerErrorLocation()
4848
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
4949
$location = [];
5050
$ns = 'OpenActive\\Models\\OA\\';
51+
$rpdeNs = 'OpenActive\\Rpde\\';
5152
foreach ($trace as $call) {
52-
if (0 !== strpos($call['class'], $ns)) {
53+
$loc = isset($call['class']) ? $call['class'] : '';
54+
if (0 === strpos($loc, $rpdeNs) && !in_array($loc, $location)) {
55+
$location[] = str_replace($ns, '', $loc);
56+
continue;
57+
}
58+
if (0 !== strpos($loc, $ns)) {
5359
continue;
5460
}
5561
$location[] = preg_replace('/set([A-Z])/', '$1', $call['function']);
56-
$location[] = str_replace($ns, '', $call['class']);
62+
$location[] = str_replace($ns, '', $loc);
5763
}
5864

5965
return implode('.', array_reverse($location));

src/Rpde/RpdeBody.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,7 @@ class RpdeBody implements SerializerInterface, TypeCheckerInterface
5050
private function __construct($data)
5151
{
5252
foreach ($data as $key => $value) {
53-
// Make sure setter is cased properly
54-
$methodName = "set" . Str::pascal($key);
55-
56-
if (method_exists($this, $methodName) === true) {
57-
$this->$methodName($value);
58-
}
53+
$this->defineProperty($key, $value);
5954
}
6055
}
6156

@@ -300,9 +295,9 @@ public function getItems()
300295
*/
301296
public function setItems($items)
302297
{
303-
$types = array(
298+
$types = [
304299
"\OpenActive\Rpde\RpdeItem[]",
305-
);
300+
];
306301

307302
$items = self::checkTypes($items, $types);
308303

@@ -331,4 +326,19 @@ public function setLicense($license)
331326

332327
$this->license = $license;
333328
}
329+
330+
public function defineProperty($key, $value)
331+
{
332+
// Ignore properties which start with @
333+
if ('@' === $key{0}) {
334+
return;
335+
}
336+
337+
// Make sure setter is cased properly
338+
$methodName = "set" . Str::pascal($key);
339+
340+
if (method_exists($this, $methodName) === true) {
341+
$this->$methodName($value);
342+
}
343+
}
334344
}

src/Rpde/RpdeItem.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ public function setData($data)
176176
{
177177
$types = array(
178178
"\OpenActive\BaseModel",
179+
"\\OpenActive\\Rpde\\RpdeItemData",
179180
"null",
180181
);
181182

src/Validators/ArrayOfValidator.php

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,11 @@ public function __construct($itemValidator)
2626
*/
2727
public function coerce($value)
2828
{
29-
// NOTE: OpenActive is more strict than schema.org in this regard, so commenting out this for now
30-
// $nullValidator = new NullValidator();
31-
32-
// If we are providing a single item of the itemValidator type (or null)
33-
// Put the value inside an array
34-
// if(
35-
// $nullValidator->run($value) === true ||
36-
// $this->itemValidator->run($value) === true
37-
// ) {
38-
// return [$value];
39-
// }
40-
41-
// Otherwise this is a no-op
42-
return $value;
29+
$newValue = [];
30+
foreach ($value as $key => $item) {
31+
$newValue[$key] = $this->itemValidator->coerce($item);
32+
}
33+
return $newValue;
4334
}
4435

4536
/**
@@ -52,16 +43,6 @@ public function run($value)
5243
{
5344
$nullValidator = new NullValidator();
5445

55-
// NOTE: OpenActive is more strict than schema.org in this regard, so commenting out this for now
56-
// If we are providing a single item of the itemValidator type (or null)
57-
// Validation passes (but the value will need to be coerced to array)
58-
// if(
59-
// $nullValidator->run($value) === true ||
60-
// $this->itemValidator->run($value) === true
61-
// ) {
62-
// return true;
63-
// }
64-
6546
// Check if value is an array
6647
if ((new ArrayValidator())->run($value) === false) {
6748
return false;

src/Validators/BaseValidator.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ public static function getValidator($type)
9090
return new RpdeEnumValidator($type);
9191
}
9292

93+
if ($type === "\\OpenActive\\Rpde\\RpdeItem") {
94+
return new RpdeItemValidator();
95+
}
96+
if ($type === "\\OpenActive\\Rpde\\RpdeItemData") {
97+
return new RpdeItemDataValidator();
98+
}
99+
93100
// If type is an OpenActive BaseModel class
94101
if ($type === "\\OpenActive\\BaseModel") {
95102
return new BaseModelValidator();
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace OpenActive\Validators;
4+
5+
use OpenActive\BaseModel;
6+
7+
class RpdeItemDataValidator implements ValidatorInterface
8+
{
9+
public function run($value)
10+
{
11+
if ($value instanceof BaseModel) {
12+
return true;
13+
}
14+
if (null === $value) {
15+
return true;
16+
}
17+
if (is_object($value)) {
18+
$value = (array) $value;
19+
}
20+
return isset($value['@type'])
21+
&& null !== $this->getValidClass($value['@type']);
22+
}
23+
24+
public function coerce($value)
25+
{
26+
if ($value instanceof BaseModel) {
27+
return $value;
28+
}
29+
if (null === $value) {
30+
return $value;
31+
}
32+
33+
$class = $this->getValidClass($value['@type']);
34+
if (null === $class) {
35+
throw new \InvalidArgumentException(sprintf('Invalid type %s given, no instantiable class', $value['@type']));
36+
}
37+
return new $class($value);
38+
}
39+
40+
private function getValidClass($type)
41+
{
42+
$classOa = sprintf('\\OpenActive\\Models\\OA\\%s', $type);
43+
$classSchema = sprintf('\\OpenActive\\Models\\SchemaOrg\\%s', $type);
44+
45+
if (class_exists($classOa)) {
46+
return $classOa;
47+
}
48+
if (class_exists($classSchema)) {
49+
return $classSchema;
50+
}
51+
return null;
52+
}
53+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace OpenActive\Validators;
4+
5+
use OpenActive\Concerns\TypeChecker;
6+
use OpenActive\Rpde\RpdeItem;
7+
8+
class RpdeItemValidator implements ValidatorInterface
9+
{
10+
use TypeChecker;
11+
12+
public function run($value)
13+
{
14+
if ($value instanceof RpdeItem) {
15+
return true;
16+
}
17+
if (is_object($value)) {
18+
$value = (array) $value;
19+
}
20+
21+
if (!isset($value['state'], $value['kind'], $value['id'], $value['modified'])) {
22+
return false;
23+
}
24+
25+
if ('deleted' === $value['state']) {
26+
return true;
27+
}
28+
29+
return isset($value['data']);
30+
}
31+
32+
public function coerce($value)
33+
{
34+
if ($value instanceof RpdeItem) {
35+
return $value;
36+
}
37+
return new RpdeItem($value);
38+
}
39+
}

tests/Unit/RpdeTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ public function testCreatesSerializedRpdeFeedPageWithAllTypes($feed, $expectedJs
4545
json_decode(RpdeBody::serialize($body))
4646
);
4747
}
48+
/**
49+
* @dataProvider allTypesDataProvider
50+
* @param array $feed
51+
* @param string $expectedJson
52+
*/
53+
public function testAbleToUnserializeRpdeFeed($feed, $expectedJson)
54+
{
55+
$body = RpdeBody::createFromModifiedId(
56+
'https://www.example.com/feed',
57+
1,
58+
"1",
59+
$feed
60+
);
61+
62+
$this->assertEquals(
63+
$body,
64+
RpdeBody::deserialize(RpdeBody::serialize($body))
65+
);
66+
}
4867

4968
/**
5069
* Test the serialized RPDE body created with createFromModifiedId returns the expected JSON-LD.

0 commit comments

Comments
 (0)