Skip to content

Commit

Permalink
adds support for int backed enums to implicit enum route binding
Browse files Browse the repository at this point in the history
  • Loading branch information
monurakkaya committed Apr 11, 2024
1 parent dec31b0 commit ede3d6a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 7 deletions.
13 changes: 10 additions & 3 deletions src/Illuminate/Routing/ImplicitRouteBinding.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
use Illuminate\Support\Reflector;
use Illuminate\Support\Str;
use ReflectionEnum;

class ImplicitRouteBinding
{
Expand Down Expand Up @@ -69,11 +70,12 @@ public static function resolveForRoute($container, $route)
/**
* Resolve the Backed Enums route bindings for the route.
*
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @return \Illuminate\Routing\Route
*
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
* @throws \ReflectionException
*/
protected static function resolveBackedEnumsForRoute($route, $parameters)
{
Expand All @@ -86,7 +88,12 @@ protected static function resolveBackedEnumsForRoute($route, $parameters)

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

$backedEnum = $backedEnumClass::tryFrom((string) $parameterValue);
$reflectionEnum = new ReflectionEnum($backedEnumClass);

match ($reflectionEnum->getBackingType()->getName()) {
'int' => $backedEnum = collect($backedEnumClass::cases())->first(fn ($case) => (string) $case->value === (string) $parameterValue),
'string' => $backedEnum = $backedEnumClass::tryFrom((string) $parameterValue),
};

if (is_null($backedEnum)) {
throw new BackedEnumCaseNotFoundException($backedEnumClass, $parameterValue);
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Routing/RouteSignatureParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static function fromAction(array $action, $conditions = [])

return match (true) {
! empty($conditions['subClass']) => array_filter($parameters, fn ($p) => Reflector::isParameterSubclassOf($p, $conditions['subClass'])),
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithStringBackingType($p)),
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithValidBackingType($p)),
default => $parameters,
};
}
Expand Down
6 changes: 3 additions & 3 deletions src/Illuminate/Support/Reflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ public static function isParameterSubclassOf($parameter, $className)
}

/**
* Determine if the parameter's type is a Backed Enum with a string backing type.
* Determine if the parameter's type is a Backed Enum with a string or int backing type.
*
* @param \ReflectionParameter $parameter
* @return bool
*/
public static function isParameterBackedEnumWithStringBackingType($parameter)
public static function isParameterBackedEnumWithValidBackingType($parameter)
{
if (! $parameter->getType() instanceof ReflectionNamedType) {
return false;
Expand All @@ -162,7 +162,7 @@ public static function isParameterBackedEnumWithStringBackingType($parameter)
$reflectionBackedEnum = new ReflectionEnum($backedEnumClass);

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

return false;
Expand Down
6 changes: 6 additions & 0 deletions tests/Routing/Enums.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ enum CategoryBackedEnum: string
case People = 'people';
case Fruits = 'fruits';
}

enum CategoryIntBackedEnum: int
{
case People = 1;
case Fruits = 2;
}
41 changes: 41 additions & 0 deletions tests/Routing/ImplicitRouteBindingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ public function test_it_can_resolve_the_implicit_backed_enum_route_bindings_for_
$this->assertSame('fruits', $route->parameter('category')->value);
}

public function test_it_can_resolve_the_implicit_int_backed_enum_route_bindings_for_the_given_route()
{
$action = ['uses' => function (CategoryIntBackedEnum $category) {
return $category->value;
}];

$route = new Route('GET', '/test', $action);
$route->parameters = ['category' => '1'];

$route->prepareForSerialization();

$container = Container::getInstance();

ImplicitRouteBinding::resolveForRoute($container, $route);

$this->assertSame(1, $route->parameter('category')->value);
}

public function test_it_can_resolve_the_implicit_backed_enum_route_bindings_for_the_given_route_with_optional_parameter()
{
$action = ['uses' => function (?CategoryBackedEnum $category = null) {
Expand Down Expand Up @@ -91,6 +109,29 @@ public function test_implicit_backed_enum_internal_exception()
ImplicitRouteBinding::resolveForRoute($container, $route);
}

public function test_implicit_int_backed_enum_internal_exception()
{
$action = ['uses' => function (CategoryIntBackedEnum $category) {
return $category->value;
}];

$route = new Route('GET', '/test', $action);
$route->parameters = ['category' => ' 00001.'];

$route->prepareForSerialization();

$container = Container::getInstance();

$this->expectException(BackedEnumCaseNotFoundException::class);
$this->expectExceptionMessage(sprintf(
'Case [%s] not found on Backed Enum [%s].',
' 00001.',
CategoryIntBackedEnum::class,
));

ImplicitRouteBinding::resolveForRoute($container, $route);
}

public function test_it_can_resolve_the_implicit_model_route_bindings_for_the_given_route()
{
$this->expectNotToPerformAssertions();
Expand Down

0 comments on commit ede3d6a

Please sign in to comment.