Dwight Watson's blog

Invalid text representation with Laravel route model binding

This blog post was originally published a little while ago. Please consider that it may no longer be relevant or even accurate.

Route model binding is a great feature to automatically fetch an Eloquent model when it's injected into a controller method - saving you having to perform the lookup and fallback to 404 yourself. Say you were to visit /posts/1, the Post with ID #1 is automatically injected into the method.

Route::get('/posts/{post}', 'PostController@show');

class PostController extends Controller
{
public function show(Post $post)
{
dd($post); // The correct will be injected from the ID in the URL
}
}

However - this feature doesn't play too nicely when used improperly with PostgreSQL. For example, say that someone makes a request like /posts/1-foo. It could be someone trying something funky or a bot trying to see what happens. Turns out with PostgreSQL this will trigger a 500 error with an invalid query.

testing.ERROR: SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "1-foo" (SQL: select * from "posts" where "posts"."id" = 1-foo limit 1) {"exception":"[object] (Illuminate\\Database\\QueryException(code: 22P02): SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: \"1-foo\" (SQL: select * from \"posts\" where \"posts\".\"id\" = 1-foo limit 1)

It's not the end of the world - but it's still a crash, and it would be a poor user experience if there was a real user on the other end of that request. There is an easy way to resolve this using Laravel's route pattern matching. In your app/Providers/RouteServiceProvider.php you can register the pattern Laravel should use to match the identifier.

class RouteServiceProvider extends ServiceProvider
{
public function boot()
{
Route::pattern('post', '[0-9]+');

parent::boot()
}
}

Now when Laravel tries to match a route with an argument labelled post it will ensure it also matches the pattern. If it doesn't it will just continue through the rest of your routes and ultimately 404 - which is probably what you wanted.

A blog about Laravel & Rails by Dwight Watson;

Picture of Dwight Watson

Follow me on Twitter, or GitHub.