Defining routes in Laravel is relatively simple, although sometimes you’ll have to deal with some tricky situations like the one I’m about to describe now.
Let’s say you have a blog with the following routes:
This could be a list of URLs that matches with those routes:
Nothing wrong there, right?
Well, if you look closer, we have a “tag route” that looks like an “article route”:
When a user tries to access the URL above, the application is going to handle that request using the ArticleController, which is not Ideal.
Now the client calls you saying that they have an increasing number of 404s on their website, and just going to the editors and telling them “you can’t create a tag that ends with a number (some-tag-123)” is not a solution.
So, you need to identify those requests and redirect to the right controller.
Note: the controller receives the parameters in the same order defined in the route:
Resolving a controller class from the container, and then, call the specific controller action, is one of the options you have to solve this issue, although it's not the ideal situation and will leave you with some loose ends all over the place:
On this example, the
ArticleController receives three parameters, but the
TagController receives only two.
What happens if, instead of passing each param in the controller, you are using the
Request object as follows:
This will introduce another problem to our current situation, remember that the request was captured by the
ArticleController in the first place, through the
That means that the current request object has three parameters:
So, if you want to redirect the same request to the
TagController when you try to access the
tag parameter, this is what you are going to get:
As you can see, you are missing the
-2 portion of the tag
What happen if we just the
You’ll get a
too many redirection error because the
redirect()->route() will construct the URL with the given params and will make a new call to that URL, not to the specific route:
The code above basically will make a call to
/movies/lord-of-the-rings-2, which is going to be captured by the
ArticleController once again.
So we need a different solution here, we need to redirect the request to a specific route
Redirecting a request to a specific route in Laravel
The key of this approach relies on the
Illuminate/Routing/Router.php class, which by the way, is the instance you get when you use the
Check out the following function from the router method:
Once the application get’s the request, the router will run the
respondWithRoute() method, to send the current request to the given route:
return $this->runRoute($this->currentRequest, $route);
runRoute method is protected, so we can't call it from outside the class.
Fortunately for us, the
Router class implements the
Macroable trait; that means that we can add macros (custom methods) to the class.
This macro will receive a request object as a first argument, and the “name” of the route that we want to handle the given request.
This is how we can use the new macro to solve our problem:
In this case, we are not only redirecting the request to a new controller-action, but actually, the URL arguments will be resolved using the given route; basically we are forcing the application to resolve the request with the specified route.
After redirecting the request with this method you can do
request()->route()->getName() and get the name of the exact route you are redirecting to, instead of the one that captured the request in the first place.
Also, the route arguments will be resolved again for the new route, and now in your
TagController you’ll get the following:
When you use the redirection methods Laravel offers out of the box, basically you are making a new call to an URL instead of forcing the application to handle the request on a specific way. Take that into consideration next time you encounter yourself in a similar situation as the one described on this article.