In a previous tip, I showed how route model binding behaves differently between MySQL and Postgres when the route segment type doesn't match the database column type.
Today I want to share one approach to fixing the problem, and also talk through whether it's even worth fixing it in the first place.
The reader who shared the issue with me also shared the way they solved it:
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (QueryException $e) {
$invalidTextRepresentation = str_starts_with($e->getMessage(), 'SQLSTATE[22P02]');
$routeBindingMethods = [
'resolveRouteBinding',
'resolveSoftDeletableRouteBinding',
'resolveChildRouteBinding',
'resolveSoftDeletableChildRouteBindings'
];
$routeBindingIssue = str_contains($e->getTraceAsString(), $routeBindingMethods);
abort_if(
boolean: $invalidTextRepresentation && $routeBindingIssue,
code: 404,
message: 'No query results for Model'
);
});
});
This works by intercepting only QueryException
exceptions, then seeing if it contains our one specific Postgres error, and finally checks if it is being thrown during route model binding.
In that one very specific circumstance, it overrides the exception to return a 404 instead of letting the exception bubble up as a 500.
One thing I like about this code sample is that it is very targeted and doesn't modify unrelated exceptions. I also like that it leverages a built-in Laravel feature and consolidates it into one spot.
But one downside is that it feels a bit removed from the source of the problem: route model binding.
And I worry that this approach could end up being fragile. For example, what if Laravel refactors how route model binding works internally, or what if a new version of Postgres has a different error code.
Also, it's worth stepping back and thinking about if we even need to fix this at all.
You could argue that if a user is monkeying around with the URL and putting invalid data into a URL, a 500 error is acceptable.
On the other hand, if this is being triggered by a poorly written bot or scraper, it could be generating a lot of noise in your error reporting system.
I don't think there's a universal right answer here, but hopefully this gives you something to think about. In the next tip, I'll share some other solutions to this problem that address the source of the issue more directly.
Here to help,
Joel
P.S. While we're thinking about bad user input, have you considered leveling up your Laravel validation knowledge?