Skip to content

Commit 6d83772

Browse files
committed
v3
1 parent dc38c3a commit 6d83772

File tree

4 files changed

+93
-38
lines changed

4 files changed

+93
-38
lines changed

src/OpenApi/PhpTypeSchemaResolver/IntPhpTypeSchemaResolver.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public function resolvePhpTypeSchema(Type $phpType, Reflector $phpTypeHolder): a
4040
return [
4141
'type' => Type::OAS_TYPE_NAME_INTEGER,
4242
'format' => PHP_INT_SIZE === 4 ? 'int32' : 'int64',
43-
'example' => 0,
4443
];
4544
}
4645

src/OpenApi/PhpTypeSchemaResolver/ObjectPhpTypeSchemaResolver.php

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace Sunrise\Http\Router\OpenApi\PhpTypeSchemaResolver;
1515

16+
use BackedEnum;
1617
use ReflectionAttribute;
1718
use ReflectionClass;
1819
use ReflectionException;
@@ -31,6 +32,7 @@
3132
use Sunrise\Hydrator\TypeConverter\ObjectTypeConverter;
3233

3334
use function class_exists;
35+
use function is_scalar;
3436
use function strtr;
3537

3638
/**
@@ -91,14 +93,25 @@ public function resolvePhpTypeSchema(Type $phpType, Reflector $phpTypeHolder): a
9193
continue;
9294
}
9395

94-
$propertyName = self::getPropertyName($property);
9596
$propertyType = TypeFactory::fromPhpTypeReflection($property->getType());
9697
$propertyTypeSchema = $this->openApiPhpTypeSchemaResolverManager
9798
->resolvePhpTypeSchema($propertyType, $property);
9899

100+
$propertyDefaultValue = self::getPropertyDefaultValue($property);
101+
$normalizePropertyDefaultValue = self::normalizePropertyDefaultValue($propertyDefaultValue);
102+
if ($normalizePropertyDefaultValue !== null) {
103+
$propertyTypeSchema = [
104+
'allOf' => [
105+
$propertyTypeSchema,
106+
],
107+
'default' => $normalizePropertyDefaultValue,
108+
];
109+
}
110+
111+
$propertyName = self::getPropertyName($property);
99112
$phpTypeSchema['properties'][$propertyName] = $propertyTypeSchema;
100113

101-
if (!self::isOptionalProperty($property)) {
114+
if (!self::hasPropertyDefaultValue($property)) {
102115
$phpTypeSchema['required'][] = $propertyName;
103116
}
104117
}
@@ -133,28 +146,61 @@ private static function getPropertyName(ReflectionProperty $property): string
133146
return $property->name;
134147
}
135148

136-
private static function isOptionalProperty(ReflectionProperty $property): bool
149+
private static function hasPropertyDefaultValue(ReflectionProperty $property): bool
137150
{
138-
if (self::hasPropertyDefaultValue($property)) {
151+
if ($property->hasDefaultValue()) {
139152
return true;
140153
}
141154

142-
if (!$property->isPromoted()) {
143-
return false;
155+
if ($property->isPromoted()) {
156+
foreach ($property->getDeclaringClass()->getConstructor()?->getParameters() ?? [] as $parameter) {
157+
if ($parameter->name === $property->name && $parameter->isDefaultValueAvailable()) {
158+
return true;
159+
}
160+
}
144161
}
145162

146-
foreach ($property->getDeclaringClass()->getConstructor()?->getParameters() ?? [] as $parameter) {
147-
if ($parameter->name === $property->name) {
148-
return $parameter->isDefaultValueAvailable();
163+
if ($property->getAttributes(DefaultValue::class) !== []) {
164+
return true;
165+
}
166+
167+
return false;
168+
}
169+
170+
private static function getPropertyDefaultValue(ReflectionProperty $property): mixed
171+
{
172+
if ($property->hasDefaultValue()) {
173+
return $property->getDefaultValue();
174+
}
175+
176+
if ($property->isPromoted()) {
177+
foreach ($property->getDeclaringClass()->getConstructor()?->getParameters() ?? [] as $parameter) {
178+
if ($parameter->name === $property->name && $parameter->isDefaultValueAvailable()) {
179+
return $parameter->getDefaultValue();
180+
}
149181
}
150182
}
151183

152-
// Will never get here...
153-
return false; // @codeCoverageIgnore
184+
/** @var list<ReflectionAttribute<DefaultValue>> $annotations */
185+
$annotations = $property->getAttributes(DefaultValue::class);
186+
if (isset($annotations[0])) {
187+
$annotation = $annotations[0]->newInstance();
188+
return $annotation->value;
189+
}
190+
191+
return null;
154192
}
155193

156-
private static function hasPropertyDefaultValue(ReflectionProperty $property): bool
194+
private static function normalizePropertyDefaultValue(mixed $value): mixed
157195
{
158-
return $property->hasDefaultValue() || $property->getAttributes(DefaultValue::class) !== [];
196+
if (is_scalar($value)) {
197+
return $value;
198+
}
199+
200+
if ($value instanceof BackedEnum) {
201+
return $value->value;
202+
}
203+
204+
return null;
159205
}
160206
}

tests/Fixture/App/Dto/Product/ProductCreateRequest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ public function __construct(
3434
public readonly ProductTagDtoCollection $tags,
3535
#[Subtype(ProductFeature::class, limit: 2)]
3636
public readonly array $features,
37-
public readonly ProductStatus $status,
3837
public readonly bool $isModerated,
3938
#[Alias('timezone')]
4039
public readonly DateTimeZone $clientTimezone,
4140
#[Format(DomainAgreement::DEFAULT_INPUT_TIMESTAMP_FORMAT)]
4241
// support for php7, not recommended in the context of this library.
4342
#[DefaultValue(new DateTimeImmutable('now'))]
4443
public readonly DateTimeImmutable $createdAt,
44+
public readonly ProductStatus $status = ProductStatus::Disabled,
4545
#[Ignore]
4646
public readonly string $permanentProperty = 'value',
4747
) {

tests/Fixture/App/openapi.json

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -322,17 +322,6 @@
322322
"free-delivery"
323323
]
324324
},
325-
"Sunrise.Http.Router.Tests.Fixture.App.Dictionary.ProductStatus": {
326-
"type": "integer",
327-
"format": "int64",
328-
"example": 0,
329-
"enum": [
330-
0,
331-
1,
332-
2,
333-
3
334-
]
335-
},
336325
"DateTimeZone": {
337326
"type": "string",
338327
"enum": [
@@ -756,6 +745,16 @@
756745
"UTC"
757746
]
758747
},
748+
"Sunrise.Http.Router.Tests.Fixture.App.Dictionary.ProductStatus": {
749+
"type": "integer",
750+
"format": "int64",
751+
"enum": [
752+
0,
753+
1,
754+
2,
755+
3
756+
]
757+
},
759758
"Sunrise.Http.Router.Tests.Fixture.App.Dto.Product.ProductCreateRequest": {
760759
"type": "object",
761760
"additionalProperties": false,
@@ -767,8 +766,7 @@
767766
"dKQLn8yyMsYG": [],
768767
"publicId": {
769768
"type": "integer",
770-
"format": "int64",
771-
"example": 0
769+
"format": "int64"
772770
},
773771
"name": {
774772
"type": "string"
@@ -815,9 +813,6 @@
815813
}
816814
]
817815
},
818-
"status": {
819-
"$ref": "#/components/schemas/Sunrise.Http.Router.Tests.Fixture.App.Dictionary.ProductStatus"
820-
},
821816
"isModerated": {
822817
"type": "boolean"
823818
},
@@ -828,6 +823,14 @@
828823
"type": "string",
829824
"format": "date-time",
830825
"example": "1970-01-01T00:00:00.000+00:00"
826+
},
827+
"status": {
828+
"allOf": [
829+
{
830+
"$ref": "#/components/schemas/Sunrise.Http.Router.Tests.Fixture.App.Dictionary.ProductStatus"
831+
}
832+
],
833+
"default": 0
831834
}
832835
},
833836
"required": [
@@ -841,7 +844,6 @@
841844
"categoryId",
842845
"tags",
843846
"features",
844-
"status",
845847
"isModerated",
846848
"timezone"
847849
]
@@ -867,14 +869,22 @@
867869
"additionalProperties": false,
868870
"properties": {
869871
"limit": {
870-
"type": "integer",
871-
"format": "int64",
872-
"example": 0
872+
"allOf": [
873+
{
874+
"type": "integer",
875+
"format": "int64"
876+
}
877+
],
878+
"default": 20
873879
},
874880
"offset": {
875-
"type": "integer",
876-
"format": "int64",
877-
"example": 0
881+
"allOf": [
882+
{
883+
"type": "integer",
884+
"format": "int64"
885+
}
886+
],
887+
"default": 0
878888
}
879889
}
880890
},

0 commit comments

Comments
 (0)