5
5
namespace Astral \Serialize \OpenApi \Storage \OpenAPI ;
6
6
7
7
use Astral \Serialize \OpenApi \Collections \ParameterCollection ;
8
+ use Astral \Serialize \OpenApi \Enum \ParameterTypeEnum ;
8
9
use Astral \Serialize \OpenApi \Storage \StorageInterface ;
9
- use Exception ;
10
10
11
11
/**
12
- * 文档开发者介绍
12
+ * OpenAPI Schema 数据存储和构建器
13
+ * 用于生成符合 OpenAPI 规范的 Schema 结构
13
14
*/
14
15
class SchemaStorage implements StorageInterface
15
16
{
16
- private array |SchemaStorage $ data = [
17
+ /**
18
+ * Schema 数据结构
19
+ */
20
+ private array $ data = [
17
21
'type ' => 'object ' ,
18
22
'properties ' => [],
19
23
];
20
24
21
- public function getData (): SchemaStorage |array
25
+ /**
26
+ * 获取构建的 Schema 数据
27
+ */
28
+ public function getData (): array
22
29
{
23
30
return $ this ->data ;
24
31
}
25
32
26
33
/**
27
- * Undocumented function
34
+ * 构建 OpenAPI Schema 数据结构
28
35
*
29
- * @param array<string, ParameterCollection> $tree
30
- * @param mixed |null $node
31
- * @return SchemaStorage
36
+ * @param array<ParameterCollection> $parameterTree 参数集合树
37
+ * @param array |null $currentNode 当前构建节点的引用
38
+ * @return static
32
39
*/
33
- public function build (array $ tree , mixed &$ node = null ): static
40
+ public function build (array $ parameterTree , array &$ currentNode = null ): static
34
41
{
35
- if ($ node === null ) {
36
- $ node = &$ this ->data ;
42
+ if ($ currentNode === null ) {
43
+ $ currentNode = &$ this ->data ;
37
44
}
38
45
39
- foreach ($ tree as $ item ) {
46
+ foreach ($ parameterTree as $ parameter ) {
47
+
40
48
41
- if ($ item ->ignore ) {
49
+ // 跳过被标记为忽略的参数
50
+ if ($ parameter ->ignore ) {
42
51
continue ;
43
52
}
44
53
45
- $ node ['properties ' ][$ item ->name ] = [
46
- 'type ' => strtolower ($ item ->type ->getOpenApiName ()),
47
- 'description ' => $ item ->descriptions ,
48
- 'example ' => $ item ->example ,
49
- ];
54
+ // 构建基础属性 Schema
55
+ $ this ->buildBasicPropertySchema ($ parameter , $ currentNode );
50
56
51
- if ($ item ->required ) {
52
- $ node ['required ' ][] = $ item ->name ;
57
+ // oneOf/anyOf/allOf 格式
58
+ if ($ parameter ->type ->isOf ()){
59
+ $ this ->buildOfProperties ($ parameter , $ currentNode );
60
+ }
61
+ // 处理嵌套子属性
62
+ else if ($ parameter ->children ) {
63
+ $ this ->buildNestedProperties ($ parameter , $ currentNode );
53
64
}
65
+ }
54
66
55
- if ($ item ->children ) {
56
- // list对象
57
- if ($ item ->type ->isCollect ()) {
58
- $ node ['properties ' ][$ item ->name ]['items ' ] = [
59
- 'type ' => 'object ' ,
60
- 'properties ' => [],
61
- ];
62
- $ tree = &$ node ['properties ' ][$ item ->name ]['items ' ];
63
- }
64
- // 单个对象
65
- elseif ($ item ->type ->existsCollectClass ()) {
66
- $ node ['properties ' ][$ item ->name ] = [
67
- 'type ' => 'object ' ,
68
- 'properties ' => [],
69
- ];
70
- $ tree = &$ node ['properties ' ][$ item ->name ];
71
- }
67
+ return $ this ;
68
+ }
69
+
70
+ /**
71
+ * 构建基础属性的 Schema 结构
72
+ */
73
+ private function buildBasicPropertySchema (ParameterCollection $ parameter , array &$ currentNode ): void
74
+ {
75
+ $ propertyName = $ parameter ->name ;
76
+
77
+ $ currentNode ['properties ' ][$ propertyName ] = [
78
+ 'type ' => $ parameter ->type ->value ,
79
+ 'description ' => $ parameter ->descriptions ,
80
+ 'example ' => $ parameter ->example ,
81
+ ];
72
82
73
- foreach ($ item ->children as $ v ){
74
- $ this ->build ($ v , $ tree );
83
+ // 添加必填字段标记
84
+ if ($ parameter ->required ) {
85
+ $ currentNode ['required ' ][] = $ propertyName ;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * 构建 oneOf/anyOf/allOf 属性结构
91
+ */
92
+ public function buildOfProperties (ParameterCollection $ topParameter , array &$ currentNode ): void
93
+ {
94
+ $ propertyName = $ topParameter ->name ;
95
+ // 重构属性结构为 oneOf/anyOf/allOf 格式
96
+ $ node = &$ currentNode ['properties ' ][$ propertyName ][$ topParameter ->type ->value ];
97
+
98
+ $ i = 0 ;
99
+ foreach ($ topParameter ->types as $ kindType ){
100
+ $ type = ParameterTypeEnum::getBaseEnumByTypeKindEnum ($ kindType );
101
+ if ($ type ){
102
+ $ node [$ i ] = ['type ' => $ type ];
103
+ $ i ++;
104
+ }
105
+ }
106
+
107
+ if ($ topParameter ->children ){
108
+ foreach ($ topParameter ->children as $ className => $ child ){
109
+ $ type = ParameterTypeEnum::getArrayAndObjectEnumBy ($ topParameter ->types ,$ className );
110
+ if ($ type ->isObject ()){
111
+ $ node [$ i ] = ['type ' =>'object ' ,'properties ' => []];
112
+ $ childNode = &$ node [$ i ];
113
+ $ i ++;
114
+ }else if ($ type ->isArray ()){
115
+ $ node [$ i ] = ['type ' =>'array ' ,'items ' => ['type ' =>'object ' ,'properties ' => []]];
116
+ $ childNode = &$ node [$ i ]['items ' ];
117
+ $ i ++;
75
118
}
76
119
120
+ $ this ->build ($ child ,$ childNode );
77
121
}
78
122
}
79
123
80
- return $ this ;
81
124
}
82
125
83
- public function addProperties (string $ name , string $ type , string $ description , string $ example , bool $ required = false ): void
126
+ /**
127
+ * 构建嵌套属性结构
128
+ */
129
+ private function buildNestedProperties (ParameterCollection $ topParameter , array &$ currentNode ): void
84
130
{
85
- $ this ->data ['properties ' ][$ name ] = [
86
- 'type ' => $ type ,
87
- 'description ' => $ description ,
88
- 'example ' => $ example ,
89
- ];
131
+ $ propertyName = $ topParameter ->name ;
132
+ $ nestedNode = null ;
133
+
134
+ if ($ topParameter ->type ->isArray ()) {
135
+ // 数组类型:创建 items 结构
136
+ $ currentNode ['properties ' ][$ propertyName ]['items ' ] = [
137
+ 'type ' => 'object ' ,
138
+ 'properties ' => [],
139
+ ];
140
+ $ nestedNode = &$ currentNode ['properties ' ][$ propertyName ]['items ' ];
141
+ } elseif ($ topParameter ->type ->isObject ()) {
142
+ // 对象类型:重构为嵌套对象结构
143
+ $ currentNode ['properties ' ][$ propertyName ] = [
144
+ 'type ' => 'object ' ,
145
+ 'properties ' => [],
146
+ 'description ' => $ topParameter ->descriptions ,
147
+ ];
148
+ $ nestedNode = &$ currentNode ['properties ' ][$ propertyName ];
149
+ }
90
150
91
- if ($ required ) {
92
- $ this ->data ['required ' ][] = $ name ;
151
+ // 递归构建子属性
152
+ if ($ nestedNode !== null ) {
153
+ foreach ($ topParameter ->children as $ childParameter ) {
154
+ $ this ->build ($ childParameter , $ nestedNode );
155
+ }
93
156
}
94
157
}
95
-
96
- }
158
+ }
0 commit comments