Skip to content

Commit a497aa0

Browse files
committed
add construct option to object schema
1 parent f965379 commit a497aa0

5 files changed

Lines changed: 164 additions & 18 deletions

File tree

doc/Schema/ObjectSchema.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ $user = $schema->parse(['name' => 'John', 'age' => 30]);
3939
// Returns: User instance with populated properties
4040
```
4141

42+
```php
43+
readonly class User
44+
{
45+
public function __construct(public string $name, public int $age)
46+
{}
47+
}
48+
49+
$schema = $p->object([
50+
'name' => $p->string(),
51+
'age' => $p->int(),
52+
], User::class, true);
53+
54+
$user = $schema->parse(['name' => 'John', 'age' => 30]);
55+
// Returns: User instance with populated properties
56+
```
57+
4258
## Supported Input Types
4359

4460
The `ObjectSchema` accepts multiple input formats:

src/Parser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ public function literal(bool|float|int|string $literal): LiteralSchema
101101
* @param array<string, SchemaInterface> $fieldNameToSchema
102102
* @param class-string $classname
103103
*/
104-
public function object(array $fieldNameToSchema, string $classname = \stdClass::class): ObjectSchema
104+
public function object(array $fieldNameToSchema, string $classname = \stdClass::class, bool $construct = false): ObjectSchema
105105
{
106-
return new ObjectSchema($fieldNameToSchema, $classname);
106+
return new ObjectSchema($fieldNameToSchema, $classname, $construct);
107107
}
108108

109109
public function record(SchemaInterface $fieldSchema): RecordSchema

src/Schema/ObjectSchema.php

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ final class ObjectSchema extends AbstractObjectSchema implements ObjectSchemaInt
1616
* @param array<mixed, mixed> $fieldToSchema
1717
* @param class-string $classname
1818
*/
19-
public function __construct(array $fieldToSchema, private string $classname = \stdClass::class)
19+
public function __construct(array $fieldToSchema, private string $classname = \stdClass::class, private bool $construct = false)
2020
{
2121
parent::__construct($fieldToSchema);
2222
}
@@ -26,20 +26,29 @@ public function __construct(array $fieldToSchema, private string $classname = \s
2626
*/
2727
protected function parseFields(array $input, Errors $childrenErrors): object
2828
{
29-
$object = new ($this->classname);
30-
29+
$fields = [];
3130
foreach ($this->getFieldToSchema() as $fieldName => $fieldSchema) {
3231
try {
3332
if ($this->skip($input, $fieldName)) {
3433
continue;
3534
}
3635

37-
$object->{$fieldName} = $fieldSchema->parse($input[$fieldName] ?? null);
36+
$fields[$fieldName] = $fieldSchema->parse($input[$fieldName] ?? null);
3837
} catch (ErrorsException $e) {
3938
$childrenErrors->add($e->errors, $fieldName);
4039
}
4140
}
4241

43-
return $object;
42+
if (!$this->construct) {
43+
$object = new ($this->classname);
44+
45+
foreach ($fields as $fieldName => $fieldValue) {
46+
$object->{$fieldName} = $fieldValue;
47+
}
48+
49+
return $object;
50+
}
51+
52+
return new ($this->classname)(...$fields);
4453
}
4554
}

tests/Unit/ParserTest.php

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,30 @@ enum BackedSuit: string
3333
case Spades = 'S';
3434
}
3535

36+
final class ObjectDemo implements \JsonSerializable
37+
{
38+
public string $field1;
39+
40+
public function jsonSerialize(): array
41+
{
42+
return [
43+
'field1' => $this->field1,
44+
];
45+
}
46+
}
47+
48+
final readonly class ObjectConstructDemo implements \JsonSerializable
49+
{
50+
public function __construct(public string $field1) {}
51+
52+
public function jsonSerialize(): array
53+
{
54+
return [
55+
'field1' => $this->field1,
56+
];
57+
}
58+
}
59+
3660
/**
3761
* @covers \Chubbyphp\Parsing\Parser
3862
*
@@ -159,7 +183,7 @@ public function testLiteral(): void
159183
self::assertInstanceOf(LiteralSchema::class, $literalSchema);
160184
}
161185

162-
public function testObject(): void
186+
public function testObjectStdClass(): void
163187
{
164188
$p = new Parser();
165189

@@ -170,6 +194,44 @@ public function testObject(): void
170194
self::assertInstanceOf(ObjectSchema::class, $objectSchema);
171195
}
172196

197+
public function testObjectWithObject(): void
198+
{
199+
$p = new Parser();
200+
201+
$objectSchema = $p->object([
202+
'field' => $p->string(),
203+
], ObjectDemo::class, );
204+
205+
$classnameReflection = new \ReflectionProperty(ObjectSchema::class, 'classname');
206+
207+
self::assertSame(ObjectDemo::class, $classnameReflection->getValue($objectSchema));
208+
209+
$constructReflection = new \ReflectionProperty(ObjectSchema::class, 'construct');
210+
211+
self::assertFalse($constructReflection->getValue($objectSchema));
212+
213+
self::assertInstanceOf(ObjectSchema::class, $objectSchema);
214+
}
215+
216+
public function testObjectWithObjectConstruct(): void
217+
{
218+
$p = new Parser();
219+
220+
$objectSchema = $p->object([
221+
'field' => $p->string(),
222+
], ObjectConstructDemo::class, true);
223+
224+
$classnameReflection = new \ReflectionProperty(ObjectSchema::class, 'classname');
225+
226+
self::assertSame(ObjectConstructDemo::class, $classnameReflection->getValue($objectSchema));
227+
228+
$constructReflection = new \ReflectionProperty(ObjectSchema::class, 'construct');
229+
230+
self::assertTrue($constructReflection->getValue($objectSchema));
231+
232+
self::assertInstanceOf(ObjectSchema::class, $objectSchema);
233+
}
234+
173235
public function testRecord(): void
174236
{
175237
$p = new Parser();

tests/Unit/Schema/ObjectSchemaTest.php

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,28 @@ final class ObjectDemo implements \JsonSerializable
1616
{
1717
public string $field1;
1818
public int $field2;
19+
public ?float $field3;
1920

2021
public function jsonSerialize(): array
2122
{
2223
return [
2324
'field1' => $this->field1,
2425
'field2' => $this->field2,
26+
'field3' => $this->field3,
27+
];
28+
}
29+
}
30+
31+
final readonly class ObjectConstructDemo implements \JsonSerializable
32+
{
33+
public function __construct(public string $field1, public int $field2, public ?float $field3) {}
34+
35+
public function jsonSerialize(): array
36+
{
37+
return [
38+
'field1' => $this->field1,
39+
'field2' => $this->field2,
40+
'field3' => $this->field3,
2541
];
2642
}
2743
}
@@ -35,7 +51,11 @@ final class ObjectSchemaTest extends TestCase
3551
{
3652
public function testImmutability(): void
3753
{
38-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]);
54+
$schema = new ObjectSchema([
55+
'field1' => new StringSchema(),
56+
'field2' => new IntSchema(),
57+
'field3' => new FloatSchema(),
58+
]);
3959

4060
self::assertNotSame($schema, $schema->nullable());
4161
self::assertNotSame($schema, $schema->nullable(false));
@@ -78,11 +98,15 @@ public function testConstructWithoutFieldSchema(): void
7898

7999
public function testParseSuccess(): void
80100
{
81-
$input = ['field1' => 'test', 'field2' => 1];
101+
$input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159];
82102

83-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]);
103+
$schema = new ObjectSchema([
104+
'field1' => new StringSchema(),
105+
'field2' => new IntSchema(),
106+
'field3' => new FloatSchema(),
107+
]);
84108

85-
$output = $schema->parse([...$input, 'field3' => 1.5]);
109+
$output = $schema->parse($input);
86110

87111
self::assertInstanceOf(\stdClass::class, $output);
88112

@@ -91,9 +115,13 @@ public function testParseSuccess(): void
91115

92116
public function testParseSuccessWithClass(): void
93117
{
94-
$input = ['field1' => 'test', 'field2' => 1];
118+
$input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159];
95119

96-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()], ObjectDemo::class);
120+
$schema = new ObjectSchema([
121+
'field1' => new StringSchema(),
122+
'field2' => new IntSchema(),
123+
'field3' => new FloatSchema(),
124+
], ObjectDemo::class);
97125

98126
$output = $schema->parse($input);
99127

@@ -102,13 +130,35 @@ public function testParseSuccessWithClass(): void
102130
self::assertSame($input, (array) $output);
103131
}
104132

133+
public function testParseSuccessWithConstructClass(): void
134+
{
135+
$input = ['field1' => 'test', 'field2' => 1, 'field3' => 3.14159];
136+
137+
$schema = new ObjectSchema([
138+
'field1' => new StringSchema(),
139+
'field2' => new IntSchema(),
140+
'field3' => new FloatSchema(),
141+
], ObjectConstructDemo::class, true);
142+
143+
$output = $schema->parse($input);
144+
145+
self::assertInstanceOf(ObjectConstructDemo::class, $output);
146+
147+
self::assertSame($input, (array) $output);
148+
}
149+
105150
public function testParseSuccessWithStdClassInput(): void
106151
{
107152
$input = new \stdClass();
108153
$input->field1 = 'test';
109154
$input->field2 = 1;
155+
$input->field3 = 3.14159;
110156

111-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]);
157+
$schema = new ObjectSchema([
158+
'field1' => new StringSchema(),
159+
'field2' => new IntSchema(),
160+
'field3' => new FloatSchema(),
161+
]);
112162

113163
$output = $schema->parse($input);
114164

@@ -119,9 +169,13 @@ public function testParseSuccessWithStdClassInput(): void
119169

120170
public function testParseSuccessWithIteratorInput(): void
121171
{
122-
$input = new \ArrayIterator(['field1' => 'test', 'field2' => 1]);
172+
$input = new \ArrayIterator(['field1' => 'test', 'field2' => 1, 'field3' => 3.14159]);
123173

124-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]);
174+
$schema = new ObjectSchema([
175+
'field1' => new StringSchema(),
176+
'field2' => new IntSchema(),
177+
'field3' => new FloatSchema(),
178+
]);
125179

126180
$output = $schema->parse($input);
127181

@@ -135,8 +189,13 @@ public function testParseSuccessWithJsonSerialzableObject(): void
135189
$input = new ObjectDemo();
136190
$input->field1 = 'test';
137191
$input->field2 = 1;
192+
$input->field3 = 3.14159;
138193

139-
$schema = new ObjectSchema(['field1' => new StringSchema(), 'field2' => new IntSchema()]);
194+
$schema = new ObjectSchema([
195+
'field1' => new StringSchema(),
196+
'field2' => new IntSchema(),
197+
'field3' => new FloatSchema(),
198+
]);
140199

141200
$output = $schema->parse($input);
142201

0 commit comments

Comments
 (0)