Skip to content

Commit 4341063

Browse files
authored
adds support for int backed enums to implicit enum route binding (#51029)
1 parent f29e0a5 commit 4341063

File tree

5 files changed

+59
-5
lines changed

5 files changed

+59
-5
lines changed

src/Illuminate/Routing/ImplicitRouteBinding.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
99
use Illuminate\Support\Reflector;
1010
use Illuminate\Support\Str;
11+
use ReflectionEnum;
1112

1213
class ImplicitRouteBinding
1314
{
@@ -74,6 +75,7 @@ public static function resolveForRoute($container, $route)
7475
* @return \Illuminate\Routing\Route
7576
*
7677
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
78+
* @throws \ReflectionException
7779
*/
7880
protected static function resolveBackedEnumsForRoute($route, $parameters)
7981
{
@@ -86,7 +88,12 @@ protected static function resolveBackedEnumsForRoute($route, $parameters)
8688

8789
$backedEnumClass = $parameter->getType()?->getName();
8890

89-
$backedEnum = $backedEnumClass::tryFrom((string) $parameterValue);
91+
$reflectionEnum = new ReflectionEnum($backedEnumClass);
92+
93+
match ($reflectionEnum->getBackingType()->getName()) {
94+
'int' => $backedEnum = collect($backedEnumClass::cases())->first(fn ($case) => (string) $case->value === (string) $parameterValue),
95+
'string' => $backedEnum = $backedEnumClass::tryFrom((string) $parameterValue),
96+
};
9097

9198
if (is_null($backedEnum)) {
9299
throw new BackedEnumCaseNotFoundException($backedEnumClass, $parameterValue);

src/Illuminate/Routing/RouteSignatureParameters.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static function fromAction(array $action, $conditions = [])
2828

2929
return match (true) {
3030
! empty($conditions['subClass']) => array_filter($parameters, fn ($p) => Reflector::isParameterSubclassOf($p, $conditions['subClass'])),
31-
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithStringBackingType($p)),
31+
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithValidBackingType($p)),
3232
default => $parameters,
3333
};
3434
}

src/Illuminate/Support/Reflector.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,12 @@ public static function isParameterSubclassOf($parameter, $className)
141141
}
142142

143143
/**
144-
* Determine if the parameter's type is a Backed Enum with a string backing type.
144+
* Determine if the parameter's type is a Backed Enum with a string or int backing type.
145145
*
146146
* @param \ReflectionParameter $parameter
147147
* @return bool
148148
*/
149-
public static function isParameterBackedEnumWithStringBackingType($parameter)
149+
public static function isParameterBackedEnumWithValidBackingType($parameter)
150150
{
151151
if (! $parameter->getType() instanceof ReflectionNamedType) {
152152
return false;
@@ -162,7 +162,7 @@ public static function isParameterBackedEnumWithStringBackingType($parameter)
162162
$reflectionBackedEnum = new ReflectionEnum($backedEnumClass);
163163

164164
return $reflectionBackedEnum->isBacked()
165-
&& $reflectionBackedEnum->getBackingType()->getName() == 'string';
165+
&& in_array($reflectionBackedEnum->getBackingType()->getName(), ['int', 'string']);
166166
}
167167

168168
return false;

tests/Routing/Enums.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ enum CategoryBackedEnum: string
1313
case People = 'people';
1414
case Fruits = 'fruits';
1515
}
16+
17+
enum CategoryIntBackedEnum: int
18+
{
19+
case People = 1;
20+
case Fruits = 2;
21+
}

tests/Routing/ImplicitRouteBindingTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ public function test_it_can_resolve_the_implicit_backed_enum_route_bindings_for_
3131
$this->assertSame('fruits', $route->parameter('category')->value);
3232
}
3333

34+
public function test_it_can_resolve_the_implicit_int_backed_enum_route_bindings_for_the_given_route()
35+
{
36+
$action = ['uses' => function (CategoryIntBackedEnum $category) {
37+
return $category->value;
38+
}];
39+
40+
$route = new Route('GET', '/test', $action);
41+
$route->parameters = ['category' => '1'];
42+
43+
$route->prepareForSerialization();
44+
45+
$container = Container::getInstance();
46+
47+
ImplicitRouteBinding::resolveForRoute($container, $route);
48+
49+
$this->assertSame(1, $route->parameter('category')->value);
50+
}
51+
3452
public function test_it_can_resolve_the_implicit_backed_enum_route_bindings_for_the_given_route_with_optional_parameter()
3553
{
3654
$action = ['uses' => function (?CategoryBackedEnum $category = null) {
@@ -91,6 +109,29 @@ public function test_implicit_backed_enum_internal_exception()
91109
ImplicitRouteBinding::resolveForRoute($container, $route);
92110
}
93111

112+
public function test_implicit_int_backed_enum_internal_exception()
113+
{
114+
$action = ['uses' => function (CategoryIntBackedEnum $category) {
115+
return $category->value;
116+
}];
117+
118+
$route = new Route('GET', '/test', $action);
119+
$route->parameters = ['category' => ' 00001.'];
120+
121+
$route->prepareForSerialization();
122+
123+
$container = Container::getInstance();
124+
125+
$this->expectException(BackedEnumCaseNotFoundException::class);
126+
$this->expectExceptionMessage(sprintf(
127+
'Case [%s] not found on Backed Enum [%s].',
128+
' 00001.',
129+
CategoryIntBackedEnum::class,
130+
));
131+
132+
ImplicitRouteBinding::resolveForRoute($container, $route);
133+
}
134+
94135
public function test_it_can_resolve_the_implicit_model_route_bindings_for_the_given_route()
95136
{
96137
$this->expectNotToPerformAssertions();

0 commit comments

Comments
 (0)