Skip to content

Commit

Permalink
[11.x] Allow enums to be passed to routes (#52561)
Browse files Browse the repository at this point in the history
* Allow enums to be passed to routes

* style

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
NickSdot and taylorotwell committed Aug 23, 2024
1 parent e2277ca commit b543d69
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Illuminate/Foundation/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ function response($content = null, $status = 200, array $headers = [])
/**
* Generate the URL to a named route.
*
* @param string $name
* @param \BackedEnum|string $name
* @param mixed $parameters
* @param bool $absolute
* @return string
Expand Down Expand Up @@ -907,7 +907,7 @@ function storage_path($path = '')
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param \BackedEnum|string $route
* @param mixed $parameters
* @param int $status
* @param array $headers
Expand Down
6 changes: 3 additions & 3 deletions src/Illuminate/Routing/Redirector.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public function secure($path, $status = 302, $headers = [])
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param \BackedEnum|string $route
* @param mixed $parameters
* @param int $status
* @param array $headers
Expand All @@ -157,7 +157,7 @@ public function route($route, $parameters = [], $status = 302, $headers = [])
/**
* Create a new redirect response to a signed named route.
*
* @param string $route
* @param \BackedEnum|string $route
* @param mixed $parameters
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param int $status
Expand All @@ -172,7 +172,7 @@ public function signedRoute($route, $parameters = [], $expiration = null, $statu
/**
* Create a new redirect response to a signed named route.
*
* @param string $route
* @param \BackedEnum|string $route
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param mixed $parameters
* @param int $status
Expand Down
18 changes: 16 additions & 2 deletions src/Illuminate/Routing/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Routing;

use BackedEnum;
use Closure;
use Illuminate\Container\Container;
use Illuminate\Http\Exceptions\HttpResponseException;
Expand All @@ -17,6 +18,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use InvalidArgumentException;
use Laravel\SerializableClosure\SerializableClosure;
use LogicException;
use Symfony\Component\Routing\Route as SymfonyRoute;
Expand Down Expand Up @@ -752,15 +754,21 @@ public function secure()
/**
* Get or set the domain for the route.
*
* @param string|null $domain
* @param \BackedEnum|string|null $domain
* @return $this|string|null
*
* @throws \InvalidArgumentException
*/
public function domain($domain = null)
{
if (is_null($domain)) {
return $this->getDomain();
}

if ($domain instanceof BackedEnum && ! is_string($domain = $domain->value)) {
throw new InvalidArgumentException('Enum must be string backed.');
}

$parsed = RouteUri::parse($domain);

$this->action['domain'] = $parsed->uri;
Expand Down Expand Up @@ -874,11 +882,17 @@ public function getName()
/**
* Add or change the route name.
*
* @param string $name
* @param \BackedEnum|string $name
* @return $this
*
* @throws \InvalidArgumentException
*/
public function name($name)
{
if ($name instanceof BackedEnum && ! is_string($name = $name->value)) {
throw new InvalidArgumentException('Enum must be string backed.');
}

$this->action['as'] = isset($this->action['as']) ? $this->action['as'].$name : $name;

return $this;
Expand Down
9 changes: 7 additions & 2 deletions src/Illuminate/Routing/RouteRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Illuminate\Routing;

use BackedEnum;
use BadMethodCallException;
use Closure;
use Illuminate\Support\Arr;
Expand All @@ -18,10 +19,10 @@
* @method \Illuminate\Routing\Route put(string $uri, \Closure|array|string|null $action = null)
* @method \Illuminate\Routing\RouteRegistrar as(string $value)
* @method \Illuminate\Routing\RouteRegistrar controller(string $controller)
* @method \Illuminate\Routing\RouteRegistrar domain(string $value)
* @method \Illuminate\Routing\RouteRegistrar domain(\BackedEnum|string $value)
* @method \Illuminate\Routing\RouteRegistrar middleware(array|string|null $middleware)
* @method \Illuminate\Routing\RouteRegistrar missing(\Closure $missing)
* @method \Illuminate\Routing\RouteRegistrar name(string $value)
* @method \Illuminate\Routing\RouteRegistrar name(\BackedEnum|string $value)
* @method \Illuminate\Routing\RouteRegistrar namespace(string|null $value)
* @method \Illuminate\Routing\RouteRegistrar prefix(string $prefix)
* @method \Illuminate\Routing\RouteRegistrar scopeBindings()
Expand Down Expand Up @@ -126,6 +127,10 @@ public function attribute($key, $value)
);
}

if ($value instanceof BackedEnum && ! is_string($value = $value->value)) {
throw new InvalidArgumentException("Attribute [{$key}] expects a string backed enum.");
}

$this->attributes[$attributeKey] = $value;

return $this;
Expand Down
12 changes: 8 additions & 4 deletions src/Illuminate/Routing/UrlGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public function formatScheme($secure = null)
/**
* Create a signed route URL for a named route.
*
* @param string $name
* @param \BackedEnum|string $name
* @param mixed $parameters
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param bool $absolute
Expand Down Expand Up @@ -402,7 +402,7 @@ protected function ensureSignedRouteParametersAreNotReserved($parameters)
/**
* Create a temporary signed route URL for a named route.
*
* @param string $name
* @param \BackedEnum|string $name
* @param \DateTimeInterface|\DateInterval|int $expiration
* @param array $parameters
* @param bool $absolute
Expand Down Expand Up @@ -491,15 +491,19 @@ public function signatureHasNotExpired(Request $request)
/**
* Get the URL to a named route.
*
* @param string $name
* @param \BackedEnum|string $name
* @param mixed $parameters
* @param bool $absolute
* @return string
*
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException|\InvalidArgumentException
*/
public function route($name, $parameters = [], $absolute = true)
{
if ($name instanceof BackedEnum && ! is_string($name = $name->value)) {
throw new InvalidArgumentException('Attribute [name] expects a string backed enum.');
}

if (! is_null($route = $this->routes->getByName($name))) {
return $this->toRoute($route, $parameters, $absolute);
}
Expand Down
8 changes: 8 additions & 0 deletions tests/Integration/Routing/RouteNameEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Illuminate\Tests\Integration\Routing;

enum RouteNameEnum: string
{
case UserIndex = 'users.index';
}
13 changes: 13 additions & 0 deletions tests/Integration/Routing/SimpleRouteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,17 @@ public function testSimpleRouteThroughTheFramework()

$this->assertSame('bar', $response->baseRequest->query('foo'));
}

public function testSimpleRouteWitStringBackedEnumRouteNameThroughTheFramework()
{
Route::get('/', function () {
return 'Hello World';
})->name(RouteNameEnum::UserIndex);

$response = $this->get(\route(RouteNameEnum::UserIndex, ['foo' => 'bar']));

$this->assertSame('Hello World', $response->content());

$this->assertSame('bar', $response->baseRequest->query('foo'));
}
}
16 changes: 16 additions & 0 deletions tests/Routing/Enums.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,19 @@ enum CategoryBackedEnum: string
case People = 'people';
case Fruits = 'fruits';
}

enum RouteNameEnum: string
{
case UserIndex = 'users.index';
}

enum RouteDomainEnum: string
{
case DashboardDomain = 'dashboard.myapp.com';
}

enum IntegerEnum: int
{
case One = 1;
case Two = 2;
}
28 changes: 28 additions & 0 deletions tests/Routing/RouteRegistrarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,34 @@ public function testCanSetRouteNameUsingNameAlias()
$this->assertSame('users.index', $this->getRoute()->getName());
}

public function testCanSetRouteNameUsingStringBackedEnum()
{
$this->router->name(RouteNameEnum::UserIndex)->get('users', fn () => 'all-users');

$this->assertSame('users.index', $this->getRoute()->getName());
}

public function testCannotSetRouteNameUsingIntegerBackedEnum()
{
$this->expectExceptionObject(new \InvalidArgumentException('Attribute [name] expects a string backed enum.'));

$this->router->name(IntegerEnum::One)->get('users', fn () => 'all-users');
}

public function testCanSetRouteDomainUsingStringBackedEnum()
{
$this->router->domain(RouteDomainEnum::DashboardDomain)->get('users', fn () => 'all-users');

$this->assertSame('dashboard.myapp.com', $this->getRoute()->getDomain());
}

public function testCannotSetRouteDomainUsingIntegerBackedEnum()
{
$this->expectExceptionObject(new \InvalidArgumentException('Attribute [domain] expects a string backed enum.'));

$this->router->domain(IntegerEnum::One)->get('users', fn () => 'all-users');
}

public function testPushMiddlewareToGroup()
{
$this->router->middlewareGroup('web', []);
Expand Down
7 changes: 7 additions & 0 deletions tests/Routing/RoutingUrlGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ public function testBasicRouteGeneration()
}]);
$routes->add($route);

/*
* With backed enum name and domain
*/
$route = (new Route(['GET'], 'backed-enum', ['as' => 'prefixed.']))->name(RouteNameEnum::UserIndex)->domain(RouteDomainEnum::DashboardDomain);
$routes->add($route);

$this->assertSame('/', $url->route('plain', [], false));
$this->assertSame('/?foo=bar', $url->route('plain', ['foo' => 'bar'], false));
$this->assertSame('http://www.foo.com/foo/bar', $url->route('foo'));
Expand Down Expand Up @@ -305,6 +311,7 @@ public function testBasicRouteGeneration()
$this->assertSame('/foo/bar?foo=bar#derp', $url->route('fragment', ['foo' => 'bar'], false));
$this->assertSame('/foo/bar?baz=%C3%A5%CE%B1%D1%84#derp', $url->route('fragment', ['baz' => 'åαф'], false));
$this->assertSame('http://en.example.com/foo', $url->route('defaults'));
$this->assertSame('http://dashboard.myapp.com/backed-enum', $url->route('prefixed.users.index'));
}

public function testFluentRouteNameDefinitions()
Expand Down

0 comments on commit b543d69

Please sign in to comment.