Nested route model binding in Laravel
This blog post was originally published a little while ago. Please consider that it may no longer be relevant or even accurate.
I'm a big fan of route model binding in Laravel, and have been using it since it was first introduced. I was very excited when it later became standard with implicit route model binding in 5.2. There is just one issue that route model binding brings that is easily solved by the alternative, fetching models yourself in the action. Let's use an example of fetching a user's post.
In this example because you fetch the post through the user's posts()
association you can be sure that you're getting the post that belongs to the user. Of course in a real-world example for this sort of association you might instead consider authorization, but it's a simple solution of how to fetch a nested (or "belongs to") model.
Let's have a look at how it looks with route model binding. By default route model binding is going to be done with the model's ID, so it's going to fetch the correct model for you, but you might still want to fetch it through the association to confirm it's correct.
This isn't all too bad, however where it gets complex is when you aren't using model IDs as the route parameter. What if our Post
model used a slug for URLs, and that slug didn't need to be unique globally - just unique to that user?
Now with route model binding - we can't be sure that the correct model has been fetched. If there are multiple posts with the same slug it's just going to get the first - it's "blissfully" unaware of the association. You might compensate for this by refetching the post using the slug through the association.
However we don't want to have to go through the entire site, always re-fetching the model through an association to make sure we're getting the right one. It's brittle and bound to be missed in some places. There's a better way - we can sort it out in the RouteServiceProvider
with a manual binding.
With this, whenever Laravel tries to perform a model binding on a route it will first check to see if the route has a user
parameter, and if so it will use it to correctly scope the model lookup. Now we're back to a nice clean model action without any crap.