Skip to content

Commit ab7f5d1

Browse files
committed
RC1
1 parent 3e0d541 commit ab7f5d1

File tree

6 files changed

+209
-67
lines changed

6 files changed

+209
-67
lines changed

src/OpenApi/Collections/OpenApiCollection.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,23 @@ public function buildRequestBodyParameterCollections(string $className, array $g
105105
$serializeContext->from();
106106
$properties = $serializeContext->getGroupCollection()->getProperties();
107107

108+
108109
$vols = [];
109110
foreach ($properties as $property){
110111
$vol = new ParameterCollection(
112+
className: $className,
111113
name: current($property->getInputNamesByGroups($groups,$className)),
112114
descriptions: '',
115+
types: $property->getTypes(),
113116
type: ParameterTypeEnum::getByTypes($property->getTypes()),
114117
required: !$property->isNullable(),
115118
ignore: false,
116119
);
117120

118121
if($property->getChildren()){
119122
foreach ($property->getChildren() as $children){
120-
$vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName());
123+
$className = $children->getClassName();
124+
$vol->children[$className] = $this->buildRequestBodyParameterCollections($className);
121125
}
122126
}
123127

@@ -144,16 +148,18 @@ public function buildResponseParameterCollections(): array
144148
return [];
145149
}
146150

147-
$groups = $this->response ? $this->response->groups : ['default'];
151+
$groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default'];
148152
$serializeContext = ContextFactory::build($responseClass);
149153
$serializeContext->from();
150154
$properties = $serializeContext->getGroupCollection()->getProperties();
151155

152156
$vols = [];
153157
foreach ($properties as $property){
154158
$vol = new ParameterCollection(
159+
className: $responseClass,
155160
name:current($property->getOutNamesByGroups($groups,$responseClass)),
156161
descriptions: '',
162+
types: $property->getTypes(),
157163
type: ParameterTypeEnum::getByTypes($property->getTypes()),
158164
required: !$property->isNullable(),
159165
ignore: false,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Astral\Serialize\OpenApi\Collections;
4+
5+
6+
use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum;
7+
8+
class ParameterChildrenCollection
9+
{
10+
public function __construct(
11+
public ParameterTypeEnum $type = ParameterTypeEnum::STRING,
12+
/** @var array<int, ParameterCollection[]> $children */
13+
public array $children = [],
14+
)
15+
{
16+
}
17+
}

src/OpenApi/Collections/ParameterCollection.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,37 @@
66

77
use Astral\Serialize\Enums\TypeKindEnum;
88
use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum;
9+
use Astral\Serialize\Support\Collections\TypeCollection;
910
use Attribute;
1011

1112
class ParameterCollection
1213
{
1314
public function __construct(
15+
public string $className,
1416
/** @var string 元素变量名 */
1517
public string $name,
1618
/** @var string descriptions */
1719
public string $descriptions = '',
18-
public ParameterTypeEnum $type = ParameterTypeEnum::STRING,
20+
/** @var TypeCollection[] $types */
21+
public array $types,
22+
public ParameterTypeEnum $type,
1923
/** @var mixed 示例值 */
2024
public mixed $example = '',
2125
/** @var bool 是否必填 */
2226
public bool $required = false,
2327
/** @var bool 是否忽略显示 */
2428
public bool $ignore = false,
25-
/** @var array<string, ParameterCollection> $children */
29+
/** @var array<ParameterCollection[]> $children */
2630
public array $children = [],
2731
){
2832
}
33+
34+
public function addChildren(array $collections,ParameterTypeEnum $type = ParameterTypeEnum::STRING): void
35+
{
36+
$children = new ParameterChildrenCollection();
37+
$children->type = $type;
38+
$children->children = $collections;
39+
$this->children[] = $children;
40+
}
2941
}
42+

src/OpenApi/Enum/ParameterTypeEnum.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,56 @@ enum ParameterTypeEnum: string
1717
case ANY_OF = 'anyOf';
1818
case ALL_OF = 'allOf';
1919

20+
public function isObject(): bool
21+
{
22+
return $this === self::OBJECT;
23+
}
24+
25+
public function isArray(): bool
26+
{
27+
return $this === self::ARRAY;
28+
}
29+
30+
public function isOf(): bool
31+
{
32+
return $this === self::ONE_OF || $this === self::ANY_OF || $this === self::ALL_OF;
33+
}
34+
35+
public static function getBaseEnumByTypeKindEnum(TypeCollection $collection, string $className = null): ?ParameterTypeEnum
36+
{
37+
return match (true){
38+
$collection->kind === TypeKindEnum::STRING && !$className => self::STRING,
39+
$collection->kind === TypeKindEnum::INT && !$className => self::INTEGER,
40+
$collection->kind === TypeKindEnum::FLOAT && !$className => self::NUMBER,
41+
$collection->kind === TypeKindEnum::BOOLEAN && !$className=> self::BOOLEAN,
42+
in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true) && $className === $collection->className => self::OBJECT,
43+
in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true) && $className === $collection->className => self::ARRAY,
44+
default => null,
45+
};
46+
}
47+
48+
/**
49+
* @param TypeCollection[] $types
50+
* @param string $className
51+
* @return ParameterTypeEnum|null
52+
*/
53+
54+
public static function getArrayAndObjectEnumBy(array $types, string $className): ?ParameterTypeEnum
55+
{
56+
57+
foreach ($types as $collection){
58+
if($className === $collection->className && in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true)){
59+
return self::OBJECT;
60+
}
61+
62+
if( $className === $collection->className && in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true)){
63+
return self::ARRAY;
64+
}
65+
}
66+
67+
return null;
68+
}
69+
2070
/**
2171
* @param TypeCollection[] $types
2272
*/

src/OpenApi/Storage/OpenAPI/SchemaStorage.php

Lines changed: 112 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,92 +5,154 @@
55
namespace Astral\Serialize\OpenApi\Storage\OpenAPI;
66

77
use Astral\Serialize\OpenApi\Collections\ParameterCollection;
8+
use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum;
89
use Astral\Serialize\OpenApi\Storage\StorageInterface;
9-
use Exception;
1010

1111
/**
12-
* 文档开发者介绍
12+
* OpenAPI Schema 数据存储和构建器
13+
* 用于生成符合 OpenAPI 规范的 Schema 结构
1314
*/
1415
class SchemaStorage implements StorageInterface
1516
{
16-
private array|SchemaStorage $data = [
17+
/**
18+
* Schema 数据结构
19+
*/
20+
private array $data = [
1721
'type' => 'object',
1822
'properties' => [],
1923
];
2024

21-
public function getData(): SchemaStorage|array
25+
/**
26+
* 获取构建的 Schema 数据
27+
*/
28+
public function getData(): array
2229
{
2330
return $this->data;
2431
}
2532

2633
/**
27-
* Undocumented function
34+
* 构建 OpenAPI Schema 数据结构
2835
*
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
3239
*/
33-
public function build(array $tree, mixed &$node = null): static
40+
public function build(array $parameterTree, array &$currentNode = null): static
3441
{
35-
if ($node === null) {
36-
$node = &$this->data;
42+
if ($currentNode === null) {
43+
$currentNode = &$this->data;
3744
}
3845

39-
foreach ($tree as $item) {
46+
foreach ($parameterTree as $parameter) {
47+
4048

41-
if ($item->ignore) {
49+
// 跳过被标记为忽略的参数
50+
if ($parameter->ignore) {
4251
continue;
4352
}
4453

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);
5056

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);
5364
}
65+
}
5466

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+
];
7282

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++;
75118
}
76119

120+
$this->build($child,$childNode);
77121
}
78122
}
79123

80-
return $this;
81124
}
82125

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
84130
{
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+
}
90150

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+
}
93156
}
94157
}
95-
96-
}
158+
}

0 commit comments

Comments
 (0)