Skip to content

Commit cb4406e

Browse files
committed
refactor: adds abstract DTO with json serialization
1 parent 18ffdb4 commit cb4406e

19 files changed

+697
-59
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace WordPress\AiClient\Common;
6+
7+
use JsonSerializable;
8+
use stdClass;
9+
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
10+
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
11+
12+
/**
13+
* Abstract base class for all Data Value Objects in the AI Client.
14+
*
15+
* This abstract class consolidates the common functionality needed by all
16+
* data transfer objects:
17+
* - Array transformation for data manipulation
18+
* - JSON schema support for validation and documentation
19+
* - JSON serialization with proper empty object handling
20+
*
21+
* All DTOs in the AI Client should extend this class to ensure
22+
* consistent behavior across the codebase.
23+
*
24+
* @since n.e.x.t
25+
*
26+
* @template TArrayShape of array<string, mixed>
27+
* @implements WithArrayTransformationInterface<TArrayShape>
28+
*/
29+
abstract class AbstractDataValueObject implements
30+
WithArrayTransformationInterface,
31+
WithJsonSchemaInterface,
32+
JsonSerializable
33+
{
34+
/**
35+
* Converts the object to a JSON-serializable format.
36+
*
37+
* This method uses the toArray() method and then processes the result
38+
* based on the JSON schema to ensure proper object representation for
39+
* empty arrays.
40+
*
41+
* @since n.e.x.t
42+
*
43+
* @return mixed The JSON-serializable representation.
44+
*/
45+
#[\ReturnTypeWillChange]
46+
public function jsonSerialize()
47+
{
48+
$data = $this->toArray();
49+
$schema = static::getJsonSchema();
50+
51+
return $this->convertEmptyArraysToObjects($data, $schema);
52+
}
53+
54+
/**
55+
* Recursively converts empty arrays to stdClass objects where the schema expects objects.
56+
*
57+
* @since n.e.x.t
58+
*
59+
* @param mixed $data The data to process.
60+
* @param array<mixed, mixed> $schema The JSON schema for the data.
61+
* @return mixed The processed data.
62+
*/
63+
private function convertEmptyArraysToObjects($data, array $schema)
64+
{
65+
// If data is an empty array and schema expects object, convert to stdClass
66+
if (is_array($data) && empty($data) && isset($schema['type']) && $schema['type'] === 'object') {
67+
return new stdClass();
68+
}
69+
70+
// If data is an array with content, recursively process nested structures
71+
if (is_array($data)) {
72+
// Handle object properties
73+
if (isset($schema['properties']) && is_array($schema['properties'])) {
74+
foreach ($data as $key => $value) {
75+
if (isset($schema['properties'][$key]) && is_array($schema['properties'][$key])) {
76+
$data[$key] = $this->convertEmptyArraysToObjects($value, $schema['properties'][$key]);
77+
}
78+
}
79+
}
80+
81+
// Handle array items
82+
if (isset($schema['items']) && is_array($schema['items'])) {
83+
foreach ($data as $index => $item) {
84+
$data[$index] = $this->convertEmptyArraysToObjects($item, $schema['items']);
85+
}
86+
}
87+
88+
// Handle oneOf schemas - just use the first one
89+
if (isset($schema['oneOf']) && is_array($schema['oneOf'])) {
90+
foreach ($schema['oneOf'] as $possibleSchema) {
91+
if (is_array($possibleSchema)) {
92+
return $this->convertEmptyArraysToObjects($data, $possibleSchema);
93+
}
94+
}
95+
}
96+
}
97+
98+
return $data;
99+
}
100+
}

src/Files/DTO/File.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
namespace WordPress\AiClient\Files\DTO;
66

7-
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
8-
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
7+
use WordPress\AiClient\Common\AbstractDataValueObject;
98
use WordPress\AiClient\Files\Enums\FileTypeEnum;
109
use WordPress\AiClient\Files\ValueObjects\MimeType;
1110

@@ -24,9 +23,9 @@
2423
* base64Data?: string
2524
* }
2625
*
27-
* @implements WithArrayTransformationInterface<FileArrayShape>
26+
* @extends AbstractDataValueObject<FileArrayShape>
2827
*/
29-
final class File implements WithJsonSchemaInterface, WithArrayTransformationInterface
28+
final class File extends AbstractDataValueObject
3029
{
3130
/**
3231
* @var MimeType The MIME type of the file.

src/Messages/DTO/Message.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
namespace WordPress\AiClient\Messages\DTO;
66

7-
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
8-
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
7+
use WordPress\AiClient\Common\AbstractDataValueObject;
98
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
109

1110
/**
@@ -23,9 +22,9 @@
2322
* parts: array<MessagePartArrayShape>
2423
* }
2524
*
26-
* @implements WithArrayTransformationInterface<MessageArrayShape>
25+
* @extends AbstractDataValueObject<MessageArrayShape>
2726
*/
28-
class Message implements WithJsonSchemaInterface, WithArrayTransformationInterface
27+
class Message extends AbstractDataValueObject
2928
{
3029
/**
3130
* @var MessageRoleEnum The role of the message sender.

src/Messages/DTO/MessagePart.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
namespace WordPress\AiClient\Messages\DTO;
66

7-
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
8-
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
7+
use WordPress\AiClient\Common\AbstractDataValueObject;
98
use WordPress\AiClient\Files\DTO\File;
109
use WordPress\AiClient\Messages\Enums\MessagePartTypeEnum;
1110
use WordPress\AiClient\Tools\DTO\FunctionCall;
@@ -31,9 +30,9 @@
3130
* functionResponse?: FunctionResponseArrayShape
3231
* }
3332
*
34-
* @implements WithArrayTransformationInterface<MessagePartArrayShape>
33+
* @extends AbstractDataValueObject<MessagePartArrayShape>
3534
*/
36-
final class MessagePart implements WithJsonSchemaInterface, WithArrayTransformationInterface
35+
final class MessagePart extends AbstractDataValueObject
3736
{
3837
/**
3938
* @var MessagePartTypeEnum The type of this message part.

src/Messages/DTO/ModelMessage.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace WordPress\AiClient\Messages\DTO;
66

77
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
8+
use WordPress\AiClient\Common\Traits\HasJsonSerialization;
89

910
/**
1011
* Represents a message from the AI model.

src/Messages/DTO/SystemMessage.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace WordPress\AiClient\Messages\DTO;
66

77
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
8+
use WordPress\AiClient\Common\Traits\HasJsonSerialization;
89

910
/**
1011
* Represents a system instruction message.

src/Messages/DTO/UserMessage.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace WordPress\AiClient\Messages\DTO;
66

77
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
8+
use WordPress\AiClient\Common\Traits\HasJsonSerialization;
89

910
/**
1011
* Represents a message from a user.

src/Operations/Contracts/OperationInterface.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
namespace WordPress\AiClient\Operations\Contracts;
66

7-
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
8-
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
97
use WordPress\AiClient\Operations\Enums\OperationStateEnum;
108

119
/**
@@ -15,12 +13,8 @@
1513
* They provide a way to track the progress and retrieve results asynchronously.
1614
*
1715
* @since n.e.x.t
18-
*
19-
* @template TArrayShape of array<string, mixed>
20-
*
21-
* @extends WithArrayTransformationInterface<TArrayShape>
2216
*/
23-
interface OperationInterface extends WithJsonSchemaInterface, WithArrayTransformationInterface
17+
interface OperationInterface
2418
{
2519
/**
2620
* Gets the operation ID.

src/Operations/DTO/GenerativeAiOperation.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace WordPress\AiClient\Operations\DTO;
66

7+
use WordPress\AiClient\Common\AbstractDataValueObject;
78
use WordPress\AiClient\Operations\Contracts\OperationInterface;
89
use WordPress\AiClient\Operations\Enums\OperationStateEnum;
910
use WordPress\AiClient\Results\DTO\GenerativeAiResult;
@@ -20,9 +21,9 @@
2021
*
2122
* @phpstan-type GenerativeAiOperationArrayShape array{id: string, state: string, result?: GenerativeAiResultArrayShape}
2223
*
23-
* @implements OperationInterface<GenerativeAiOperationArrayShape>
24+
* @extends AbstractDataValueObject<GenerativeAiOperationArrayShape>
2425
*/
25-
final class GenerativeAiOperation implements OperationInterface
26+
final class GenerativeAiOperation extends AbstractDataValueObject implements OperationInterface
2627
{
2728
/**
2829
* @var string Unique identifier for this operation.

src/Results/Contracts/ResultInterface.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
namespace WordPress\AiClient\Results\Contracts;
66

7-
use WordPress\AiClient\Common\Contracts\WithJsonSchemaInterface;
8-
use WordPress\AiClient\Common\Contracts\WithArrayTransformationInterface;
97
use WordPress\AiClient\Results\DTO\TokenUsage;
108

119
/**
@@ -15,12 +13,8 @@
1513
* such as token usage and provider-specific information.
1614
*
1715
* @since n.e.x.t
18-
*
19-
* @template TArrayShape of array<string, mixed>
20-
*
21-
* @extends WithArrayTransformationInterface<TArrayShape>
2216
*/
23-
interface ResultInterface extends WithJsonSchemaInterface, WithArrayTransformationInterface
17+
interface ResultInterface
2418
{
2519
/**
2620
* Gets the result ID.

0 commit comments

Comments
 (0)