Skip to content

Commit 03d159b

Browse files
committed
Add discriminator map default type support
1 parent e06cc5c commit 03d159b

6 files changed

+75
-8
lines changed

src/Mapping/DiscriminatorMap.php

+20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace TypeLang\Mapper\Mapping;
66

7+
use JetBrains\PhpStorm\Language;
8+
79
/**
810
* ```
911
* #[DiscriminatorMap(field: 'type', map: [
@@ -24,12 +26,30 @@ class DiscriminatorMap
2426
{
2527
public function __construct(
2628
/**
29+
* The property holding the type discriminator
30+
*
2731
* @var non-empty-string
2832
*/
2933
public readonly string $field,
3034
/**
35+
* The mapping between field value and types, i.e.
36+
*
37+
* ```
38+
* [
39+
* 'admin_user' => AdminUser::class,
40+
* 'admin_users' => 'list<AdminUser>',
41+
* ]
42+
*
3143
* @var non-empty-array<non-empty-string, non-empty-string>
3244
*/
3345
public readonly array $map,
46+
/**
47+
* Default type if the discriminator field ({@see $field}) is missing
48+
* or does not match the mapping rules ({@see $map})
49+
*
50+
* @var non-empty-string|null
51+
*/
52+
#[Language('PHP')]
53+
public readonly ?string $otherwise = null,
3454
) {}
3555
}

src/Mapping/Driver/AttributeDriver.php

+11
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ protected function load(
145145
$attribute = $this->findClassAttribute($reflection, DiscriminatorMap::class);
146146
if ($attribute !== null) {
147147
$mapping = [];
148+
$default = null;
148149

149150
foreach ($attribute->map as $mappedValue => $mappedType) {
150151
$mapping[$mappedValue] = $this->createDiscriminatorType(
@@ -155,9 +156,19 @@ class: $reflection,
155156
);
156157
}
157158

159+
if ($attribute->otherwise !== null) {
160+
$default = $this->createDiscriminatorType(
161+
type: $attribute->otherwise,
162+
class: $reflection,
163+
types: $types,
164+
parser: $parser,
165+
);
166+
}
167+
158168
$class->setDiscriminator(new DiscriminatorMapMetadata(
159169
field: $attribute->field,
160170
map: $mapping,
171+
default: $default,
161172
));
162173
}
163174
}

src/Mapping/Metadata/ClassMetadata.php

-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ public function __construct(
6161
*
6262
* Required to print type information in exceptions.
6363
*
64-
* @api
65-
*
6664
* @codeCoverageIgnore
6765
*/
6866
public function getTypeStatement(Context $context): TypeStatement

src/Mapping/Metadata/DiscriminatorMapMetadata.php

+22-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public function __construct(
2222
* @var non-empty-array<non-empty-string, TypeMetadata>
2323
*/
2424
private readonly array $map,
25+
private ?TypeMetadata $default = null,
2526
?int $createdAt = null,
2627
) {
2728
parent::__construct($createdAt);
@@ -73,11 +74,30 @@ public function getField(): string
7374
}
7475

7576
/**
76-
* Dynamically creates AST discriminator representation.
77+
* Returns default mapping type for transformations that do not comply
78+
* with the specified mapping rules defined in {@see getMapping()}.
7779
*
78-
* Required to print type information in exceptions.
80+
* @api
81+
*/
82+
public function getDefaultType(): ?TypeMetadata
83+
{
84+
return $this->default;
85+
}
86+
87+
/**
88+
* Updates default mapping type.
7989
*
8090
* @api
91+
*/
92+
public function setDefaultType(?TypeMetadata $type): void
93+
{
94+
$this->default = $type;
95+
}
96+
97+
/**
98+
* Dynamically creates AST discriminator representation.
99+
*
100+
* Required to print type information in exceptions.
81101
*
82102
* @codeCoverageIgnore
83103
*/

src/Mapping/NormalizeAsArray.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ final class NormalizeAsArray
99
{
1010
public function __construct(
1111
/**
12-
* Enables normalization of an object value as an
13-
* associative {@see array} if {@see $enabled} is set
14-
* to {@see true} or use anonymous {@see object} in case of
15-
* parameter is set to {@see false}.
12+
* Enables normalization of an object value as an associative
13+
* {@see array} if {@see $enabled} is set to {@see true} or use
14+
* anonymous {@see object} in case of parameter is set to {@see false}.
1615
*/
1716
public readonly bool $enabled = true,
1817
) {}

src/Type/ClassType/ClassTypeDenormalizer.php

+19
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,19 @@ public function cast(mixed $value, Context $context): mixed
8282
*/
8383
private function castOverDiscriminator(DiscriminatorMapMetadata $map, array $value, Context $context): mixed
8484
{
85+
// Default mapping type
86+
$default = $map->getDefaultType()
87+
?->getType();
88+
8589
$field = $map->getField();
8690

8791
// In case of discriminator field is missing
8892
if (!\array_key_exists($field, $value)) {
93+
// In case of default type is present
94+
if ($default !== null) {
95+
return $default->cast($value, $context);
96+
}
97+
8998
throw MissingRequiredObjectFieldException::createFromContext(
9099
field: $field,
91100
expected: $map->getTypeStatement(),
@@ -98,6 +107,11 @@ private function castOverDiscriminator(DiscriminatorMapMetadata $map, array $val
98107

99108
// In case of discriminator field is not a string
100109
if (!\is_string($element)) {
110+
// In case of default type is present
111+
if ($default !== null) {
112+
return $default->cast($value, $context);
113+
}
114+
101115
throw InvalidObjectValueException::createFromContext(
102116
element: $element,
103117
field: $field,
@@ -111,6 +125,11 @@ private function castOverDiscriminator(DiscriminatorMapMetadata $map, array $val
111125

112126
// In case of discriminator value is not found
113127
if ($mapping === null) {
128+
// In case of default type is present
129+
if ($default !== null) {
130+
return $default->cast($value, $context);
131+
}
132+
114133
throw InvalidObjectValueException::createFromContext(
115134
element: $element,
116135
field: $field,

0 commit comments

Comments
 (0)