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”: http://localhost/movies/lord-of-the-rings-2
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: Route::get('{category}/{tag}-{id}', 'ArticleController')
.
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:
app(\App\Http\Controllers\TagController::class)->show($category, $slug.'-'.$id)
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 article
route.
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 lord-of-the-rings-2
.
What happen if we just the redirect()->route()
method?
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 Route
facade.
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);
The 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.
The solution
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:
Wrapping up
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.