Skip to content

Commit 8c3e235

Browse files
committed
Create BaseModel
1 parent 72d9588 commit 8c3e235

File tree

5 files changed

+281
-41
lines changed

5 files changed

+281
-41
lines changed

src/Mock/BaseModel.php

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
namespace OpenAPIServer\Mock;
4+
5+
use OpenAPIServer\Mock\OpenApiModelInterface;
6+
use InvalidArgumentException;
7+
use StdClass;
8+
9+
class BaseModel implements OpenApiModelInterface
10+
{
11+
/**
12+
* @var string Constant with OAS schema of current class.
13+
* Should be overwritten by inherited class.
14+
*/
15+
protected const MODEL_SCHEMA =
16+
<<<'SCHEMA'
17+
{
18+
"type" : "object",
19+
"properties": {}
20+
}
21+
SCHEMA;
22+
23+
/**
24+
* @var array Data container.
25+
* PHP has restrictions on variable names, while OAS is much more permissive.
26+
* This container helps to store unusual properties like '123_prop' without renaming.
27+
*/
28+
protected $dataContainer = [];
29+
30+
/**
31+
* Gets OAS 3.0 schema mapped to current class.
32+
*
33+
* @return array|object
34+
*/
35+
public static function getOpenApiSchema()
36+
{
37+
return json_decode(static::MODEL_SCHEMA);
38+
}
39+
40+
/**
41+
* Creates new instance from provided data.
42+
*
43+
* @param mixed $data Data with values for new instance.
44+
*
45+
* @return OpenApiModelInterface
46+
*/
47+
public static function createFromData($data): OpenApiModelInterface
48+
{
49+
$instance = new static();
50+
foreach ($data as $key => $value) {
51+
// this action handles __set method
52+
$instance->{$key} = $value;
53+
}
54+
return $instance;
55+
}
56+
57+
/**
58+
* Writes data to inaccessible (protected or private) or non-existing properties.
59+
* @ref https://www.php.net/manual/en/language.oop5.overloading.php#object.set
60+
*
61+
* @param string $param Property name
62+
* @param mixed $value Property value
63+
*
64+
* @throws \InvalidArgumentException when property doesn't exist in related OAS schema
65+
*/
66+
public function __set($param, $value)
67+
{
68+
$schema = static::getOpenApiSchema();
69+
$definedProps = (property_exists($schema, 'properties')) ? $schema->properties : null;
70+
if (
71+
is_array($definedProps)
72+
&& in_array($param, array_keys($definedProps))
73+
) {
74+
$this->dataContainer[$param] = $value;
75+
return;
76+
} elseif (
77+
is_object($definedProps)
78+
&& property_exists($definedProps, $param)
79+
) {
80+
$this->dataContainer[$param] = $value;
81+
return;
82+
}
83+
84+
throw new InvalidArgumentException(
85+
sprintf('Cannot set %s property of %s model because it doesn\'t exist in related OAS schema', $param, static::class)
86+
);
87+
}
88+
89+
/**
90+
* Reads data from inaccessible (protected or private) or non-existing properties.
91+
* @ref https://www.php.net/manual/en/language.oop5.overloading.php#object.get
92+
*
93+
* @param string $param Property name
94+
*
95+
* @throws \InvalidArgumentException when property doesn't exist in related OAS schema
96+
*
97+
* @return mixed Property value
98+
*/
99+
public function __get($param)
100+
{
101+
$schema = static::getOpenApiSchema();
102+
$definedProps = (property_exists($schema, 'properties')) ? $schema->properties : [];
103+
if (property_exists($definedProps, $param)) {
104+
return $this->dataContainer[$param];
105+
}
106+
107+
throw new InvalidArgumentException(
108+
sprintf('Cannot get %s property of %s model because it doesn\'t exist in related OAS schema', $param, static::class)
109+
);
110+
}
111+
112+
/**
113+
* Serializes the object to a value that can be serialized natively by json_encode().
114+
* @ref https://www.php.net/manual/en/jsonserializable.jsonserialize.php
115+
*
116+
* @return mixed Returns data which can be serialized by json_encode(), which is a value of any type other than a resource.
117+
*/
118+
public function jsonSerialize()
119+
{
120+
$obj = new StdClass();
121+
$schema = static::getOpenApiSchema();
122+
$definedProps = (property_exists($schema, 'properties')) ? $schema->properties : [];
123+
foreach ($definedProps as $propName => $propSchema) {
124+
if (array_key_exists($propName, $this->dataContainer)) {
125+
$obj->{$propName} = $this->dataContainer[$propName];
126+
} elseif (property_exists($schema, 'required') && in_array($propName, $schema->required)) {
127+
// property is required but not set
128+
$obj->{$propName} = null;
129+
}
130+
}
131+
return $obj;
132+
}
133+
}

test/Mock/BaseModelTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace OpenAPIServer\Mock;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use OpenAPIServer\Mock\BaseModel;
7+
use OpenAPIServer\Mock\Model\CatRefTestClass;
8+
use OpenAPIServer\Mock\OpenApiModelInterface;
9+
10+
/**
11+
* @coversDefaultClass \OpenAPIServer\Mock\BaseModel
12+
*/
13+
class BaseModelTest extends TestCase
14+
{
15+
/**
16+
* @covers ::getOpenApiSchema
17+
*/
18+
public function testGetOpenApiSchema()
19+
{
20+
$schema = BaseModel::getOpenApiSchema();
21+
$this->assertTrue(is_array($schema) || is_object($schema));
22+
}
23+
24+
/**
25+
* @covers ::createFromData
26+
* @covers ::__get
27+
*/
28+
public function testCreateFromData()
29+
{
30+
$item = CatRefTestClass::createFromData([
31+
'className' => 'cheshire',
32+
'color' => 'black',
33+
'declawed' => false,
34+
]);
35+
$this->assertInstanceOf(CatRefTestClass::class, $item);
36+
$this->assertInstanceOf(OpenApiModelInterface::class, $item);
37+
$this->assertSame('cheshire', $item->className);
38+
$this->assertSame('black', $item->color);
39+
$this->assertSame(false, $item->declawed);
40+
}
41+
42+
/**
43+
* @covers ::__set
44+
* @covers ::__get
45+
*/
46+
public function testSetter()
47+
{
48+
$item = new CatRefTestClass();
49+
$item->className = 'cheshire';
50+
$item->color = 'black';
51+
$item->declawed = false;
52+
$this->assertSame('cheshire', $item->className);
53+
$this->assertSame('black', $item->color);
54+
$this->assertSame(false, $item->declawed);
55+
}
56+
57+
/**
58+
* @covers ::jsonSerialize
59+
* @dataProvider provideJsonSerializeArguments
60+
*/
61+
public function testJsonSerialize($className, $data, $expectedJson)
62+
{
63+
$item = $className::createFromData($data);
64+
$this->assertEquals($expectedJson, json_encode($item));
65+
}
66+
67+
public function provideJsonSerializeArguments()
68+
{
69+
return [
70+
'model with all props' => [
71+
CatRefTestClass::class,
72+
[
73+
'className' => 'cheshire',
74+
'color' => 'black',
75+
'declawed' => false,
76+
],
77+
json_encode([
78+
'className' => 'cheshire',
79+
'color' => 'black',
80+
'declawed' => false,
81+
]),
82+
],
83+
'model with required props' => [
84+
CatRefTestClass::class,
85+
[
86+
'className' => 'cheshire',
87+
],
88+
json_encode([
89+
'className' => 'cheshire',
90+
]),
91+
],
92+
'model with missed required prop' => [
93+
CatRefTestClass::class,
94+
[
95+
'color' => 'black',
96+
],
97+
json_encode([
98+
'className' => null,
99+
'color' => 'black',
100+
]),
101+
],
102+
];
103+
}
104+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace OpenAPIServer\Mock\Model;
4+
5+
use OpenAPIServer\Mock\BaseModel;
6+
use OpenAPIServer\Mock\MockableInterface;
7+
8+
class CatRefTestClass extends BaseModel
9+
{
10+
protected const MODEL_SCHEMA = <<<'SCHEMA'
11+
{
12+
"required" : [ "className" ],
13+
"type" : "object",
14+
"properties" : {
15+
"className" : {
16+
"type" : "string"
17+
},
18+
"color" : {
19+
"type" : "string",
20+
"default" : "red"
21+
},
22+
"declawed" : {
23+
"type" : "boolean"
24+
}
25+
},
26+
"discriminator" : {
27+
"propertyName" : "className"
28+
}
29+
}
30+
SCHEMA;
31+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace OpenAPIServer\Mock\Model;
4+
5+
class ClassWithoutGetSchemaMethod
6+
{
7+
8+
}

test/Mock/OpenApiDataMockerTest.php

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,7 @@ public function provideMockArrayInvalidArguments()
882882
public function testMockArrayWithRef($items, $expectedStructure)
883883
{
884884
$mocker = new OpenApiDataMocker();
885-
$mocker->setModelsNamespace('JohnDoesPackage\\TestModels\\');
885+
$mocker->setModelsNamespace('OpenAPIServer\\Mock\\Model\\');
886886
$arr = $mocker->mockArray($items);
887887
$this->assertIsArray($arr);
888888
$this->assertCount(1, $arr);
@@ -1038,7 +1038,7 @@ public function provideMockObjectInvalidArguments()
10381038
public function testMockObjectWithReferencedProps()
10391039
{
10401040
$mocker = new OpenApiDataMocker();
1041-
$mocker->setModelsNamespace('JohnDoesPackage\\TestModels\\');
1041+
$mocker->setModelsNamespace('OpenAPIServer\\Mock\\Model\\');
10421042
$obj = $mocker->mockObject(
10431043
(object) [
10441044
'cat' => [
@@ -1059,7 +1059,7 @@ public function testMockObjectWithReferencedProps()
10591059
public function testMockFromSchemaWithCorrectArguments($schema, $expectedType)
10601060
{
10611061
$mocker = new OpenApiDataMocker();
1062-
$mocker->setModelsNamespace('JohnDoesPackage\\TestModels\\');
1062+
$mocker->setModelsNamespace('OpenAPIServer\\Mock\\Model\\');
10631063
$data = $mocker->mockFromSchema($schema);
10641064
$this->assertInternalType($expectedType, $data);
10651065
}
@@ -1173,7 +1173,7 @@ public function provideMockFromSchemaInvalidArguments()
11731173
public function testMockFromRefWithCorrectArguments($ref, $expectedStructure)
11741174
{
11751175
$mocker = new OpenApiDataMocker();
1176-
$mocker->setModelsNamespace('JohnDoesPackage\\TestModels\\');
1176+
$mocker->setModelsNamespace('OpenAPIServer\\Mock\\Model\\');
11771177
$data = $mocker->mockFromRef($ref);
11781178
foreach ($expectedStructure as $expectedProp => $expectedType) {
11791179
$this->assertInternalType($expectedType, $data->$expectedProp);
@@ -1202,6 +1202,7 @@ public function provideMockFromRefCorrectArguments()
12021202
public function testMockFromRefWithInvalidArguments($ref)
12031203
{
12041204
$mocker = new OpenApiDataMocker();
1205+
$mocker->setModelsNamespace('OpenAPIServer\\Mock\\Model\\');
12051206
$data = $mocker->mockFromRef($ref);
12061207
}
12071208

@@ -1252,40 +1253,3 @@ public function provideSetModelsNamespaceInvalidArguments()
12521253
];
12531254
}
12541255
}
1255-
1256-
namespace JohnDoesPackage\TestModels;
1257-
1258-
// phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
1259-
final class CatRefTestClass
1260-
{
1261-
private const MODEL_SCHEMA = <<<'SCHEMA'
1262-
{
1263-
"required" : [ "className" ],
1264-
"type" : "object",
1265-
"properties" : {
1266-
"className" : {
1267-
"type" : "string"
1268-
},
1269-
"color" : {
1270-
"type" : "string",
1271-
"default" : "red"
1272-
},
1273-
"declawed" : {
1274-
"type" : "boolean"
1275-
}
1276-
},
1277-
"discriminator" : {
1278-
"propertyName" : "className"
1279-
}
1280-
}
1281-
SCHEMA;
1282-
1283-
public static function getOpenApiSchema()
1284-
{
1285-
return json_decode(static::MODEL_SCHEMA, true);
1286-
}
1287-
}
1288-
1289-
final class ClassWithoutGetSchemaMethod
1290-
{
1291-
}

0 commit comments

Comments
 (0)