Skip to content

Commit ad0a9b6

Browse files
committed
feat: convert QueryException to ModelNotFoundException in implicit route binding
When implicit route model binding encounters a QueryException (e.g., from integer overflow or type mismatch), convert it to a ModelNotFoundException instead of letting the 500 propagate. This ensures invalid route parameters like '/users/99999999999999999999' return a 404 rather than a 500.
1 parent 40b05ac commit ad0a9b6

File tree

2 files changed

+43
-11
lines changed

2 files changed

+43
-11
lines changed

src/Illuminate/Routing/ImplicitRouteBinding.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Contracts\Routing\UrlRoutable;
66
use Illuminate\Database\Eloquent\ModelNotFoundException;
7+
use Illuminate\Database\QueryException;
78
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
89
use Illuminate\Support\Reflector;
910
use Illuminate\Support\Str;
@@ -45,19 +46,23 @@ public static function resolveForRoute($container, $route)
4546
? 'resolveSoftDeletableRouteBinding'
4647
: 'resolveRouteBinding';
4748

48-
if ($parent instanceof UrlRoutable &&
49-
! $route->preventsScopedBindings() &&
50-
($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))) {
51-
$childRouteBindingMethod = $route->allowsTrashedBindings() && $instance::isSoftDeletable()
52-
? 'resolveSoftDeletableChildRouteBinding'
53-
: 'resolveChildRouteBinding';
54-
55-
if (! $model = $parent->{$childRouteBindingMethod}(
56-
$parameterName, $parameterValue, $route->bindingFieldFor($parameterName)
57-
)) {
49+
try {
50+
if ($parent instanceof UrlRoutable &&
51+
! $route->preventsScopedBindings() &&
52+
($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))) {
53+
$childRouteBindingMethod = $route->allowsTrashedBindings() && $instance::isSoftDeletable()
54+
? 'resolveSoftDeletableChildRouteBinding'
55+
: 'resolveChildRouteBinding';
56+
57+
if (! $model = $parent->{$childRouteBindingMethod}(
58+
$parameterName, $parameterValue, $route->bindingFieldFor($parameterName)
59+
)) {
60+
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
61+
}
62+
} elseif (! $model = $instance->{$routeBindingMethod}($parameterValue, $route->bindingFieldFor($parameterName))) {
5863
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
5964
}
60-
} elseif (! $model = $instance->{$routeBindingMethod}($parameterValue, $route->bindingFieldFor($parameterName))) {
65+
} catch (QueryException) {
6166
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
6267
}
6368

tests/Routing/ImplicitRouteBindingTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Illuminate\Tests\Routing;
44

5+
use Exception;
56
use Illuminate\Container\Container;
67
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Database\Eloquent\ModelNotFoundException;
9+
use Illuminate\Database\QueryException;
710
use Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException;
811
use Illuminate\Routing\ImplicitRouteBinding;
912
use Illuminate\Routing\Route;
@@ -126,9 +129,33 @@ public function test_it_can_resolve_the_implicit_model_route_bindings_for_the_gi
126129

127130
ImplicitRouteBinding::resolveForRoute($container, $route);
128131
}
132+
133+
public function testItThrowsModelNotFoundExceptionOnQueryException(): void
134+
{
135+
$action = ['uses' => function (ImplicitRouteBindingUserWithQueryException $user) {
136+
return $user;
137+
}];
138+
139+
$route = new Route('GET', '/test', $action);
140+
$route->parameters = ['user' => 'invalid-value'];
141+
142+
$route->prepareForSerialization();
143+
144+
$this->expectException(ModelNotFoundException::class);
145+
146+
ImplicitRouteBinding::resolveForRoute(Container::getInstance(), $route);
147+
}
129148
}
130149

131150
class ImplicitRouteBindingUser extends Model
132151
{
133152
//
134153
}
154+
155+
class ImplicitRouteBindingUserWithQueryException extends Model
156+
{
157+
public function resolveRouteBinding($value, $field = null): void
158+
{
159+
throw new QueryException('mysql', 'select * from users where id = ?', [$value], new Exception('Out of range value'));
160+
}
161+
}

0 commit comments

Comments
 (0)