diff --git a/src/Illuminate/Routing/ImplicitRouteBinding.php b/src/Illuminate/Routing/ImplicitRouteBinding.php index 8d597559dda..4d75630a0ef 100644 --- a/src/Illuminate/Routing/ImplicitRouteBinding.php +++ b/src/Illuminate/Routing/ImplicitRouteBinding.php @@ -46,7 +46,7 @@ public static function resolveForRoute($container, $route) ? 'resolveSoftDeletableRouteBinding' : 'resolveRouteBinding'; - if ($parent instanceof UrlRoutable && ($route->enforcesScopedBindings() || $route->bindingFieldFor($parameterName) !== null)) { + if ($parent instanceof UrlRoutable && ($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))) { $childRouteBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance)) ? 'resolveSoftDeletableChildRouteBinding' : 'resolveChildRouteBinding'; diff --git a/src/Illuminate/Routing/Route.php b/src/Illuminate/Routing/Route.php index 25be726a1ba..941a1ff0b5d 100755 --- a/src/Illuminate/Routing/Route.php +++ b/src/Illuminate/Routing/Route.php @@ -535,9 +535,11 @@ public function signatureParameters($conditions = []) */ public function bindingFieldFor($parameter) { - $fields = is_int($parameter) ? array_values($this->bindingFields) : $this->bindingFields; + if (is_int($parameter)) { + $parameter = $this->parameterNames()[$parameter]; + } - return $fields[$parameter] ?? null; + return $this->bindingFields[$parameter] ?? null; } /** diff --git a/src/Illuminate/Routing/RouteUri.php b/src/Illuminate/Routing/RouteUri.php index 113612f932e..323717741e1 100644 --- a/src/Illuminate/Routing/RouteUri.php +++ b/src/Illuminate/Routing/RouteUri.php @@ -44,27 +44,19 @@ public static function parse($uri) $bindingFields = []; foreach ($matches[0] as $match) { - $parameter = trim($match, '{}?'); - - if (! str_contains($parameter, ':')) { - $bindingFields[$parameter] = null; - + if (! str_contains($match, ':')) { continue; } - $segments = explode(':', $parameter); + $segments = explode(':', trim($match, '{}?')); $bindingFields[$segments[0]] = $segments[1]; $uri = str_contains($match, '?') - ? str_replace($match, '{'.$segments[0].'?}', $uri) - : str_replace($match, '{'.$segments[0].'}', $uri); + ? str_replace($match, '{'.$segments[0].'?}', $uri) + : str_replace($match, '{'.$segments[0].'}', $uri); } - $bindingFields = ! empty(array_filter($bindingFields)) - ? $bindingFields - : []; - return new static($uri, $bindingFields); } } diff --git a/tests/Routing/RouteUriTest.php b/tests/Routing/RouteUriTest.php index 3b0f7def220..f5790258dd3 100644 --- a/tests/Routing/RouteUriTest.php +++ b/tests/Routing/RouteUriTest.php @@ -51,27 +51,22 @@ public function uriProvider() [ '/foo/{bar}/baz/{qux:slug}', '/foo/{bar}/baz/{qux}', - ['bar' => null, 'qux' => 'slug'], + ['qux' => 'slug'], ], [ '/foo/{bar}/baz/{qux:slug}', '/foo/{bar}/baz/{qux}', - ['bar' => null, 'qux' => 'slug'], - ], - [ - '/foo/{bar:slug}/baz/{qux}', - '/foo/{bar}/baz/{qux}', - ['bar' => 'slug', 'qux' => null], + ['qux' => 'slug'], ], [ '/foo/{bar}/baz/{qux:slug?}', '/foo/{bar}/baz/{qux?}', - ['bar' => null, 'qux' => 'slug'], + ['qux' => 'slug'], ], [ '/foo/{bar}/baz/{qux:slug?}/{test:id?}', '/foo/{bar}/baz/{qux?}/{test?}', - ['bar' => null, 'qux' => 'slug', 'test' => 'id'], + ['qux' => 'slug', 'test' => 'id'], ], ]; } diff --git a/tests/Routing/RoutingRouteTest.php b/tests/Routing/RoutingRouteTest.php index 00c42519f6d..c11843d44e0 100644 --- a/tests/Routing/RoutingRouteTest.php +++ b/tests/Routing/RoutingRouteTest.php @@ -1706,6 +1706,23 @@ public function testParentChildImplicitBindingsWhereOnlySomeParametersAreScoped( $this->assertSame('2|another-test-slug|3', $router->dispatch(Request::create('foo/2/another-test-slug/3', 'GET'))->getContent()); } + public function testApiResourceScopingWhenChildDoesNotBelongToParent() + { + ResourceRegistrar::singularParameters(); + $router = $this->getRouter(); + $router->apiResource( + 'teams.users', + RouteTestNestedResourceControllerWithMissingUser::class, + ['only' => ['show']], + ) + ->middleware(SubstituteBindings::class) + ->scoped(); + + $this->expectException(ModelNotFoundException::class); + + $router->dispatch(Request::create('teams/1/users/2', 'GET')); + } + public function testParentChildImplicitBindingsProperlyCamelCased() { $router = $this->getRouter(); @@ -2109,6 +2126,13 @@ public function show(RoutingTestUserModel $fooBar) } } +class RouteTestNestedResourceControllerWithMissingUser extends Controller +{ + public function show(RoutingTestTeamWithoutUserModel $team, RoutingTestUserModel $user) + { + } +} + class RouteTestClosureMiddlewareController extends Controller { public function __construct() @@ -2366,6 +2390,14 @@ public function firstOrFail() } } +class RoutingTestTeamWithoutUserModel extends RoutingTestTeamModel +{ + public function users() + { + throw new ModelNotFoundException(); + } +} + class ActionStub { public function __invoke()