Laravel: Fixing “Route not found”

There is more to just a Route::get() call

Italo Baeza Cabrera
4 min readSep 19, 2022
Photo by Javier Allegue Barros on Unsplash

For some folks in Laravel, registering a route during the application execution is not necessary, let alone useless. The recommended way to register all the application routes is by declaring them inside the routes/web.php or routes/api.php files, as the documentation says.

Sometimes, you may need to create one “on demand”. For example, I tried to register a Route inside a test method to check if my Laragear MailLogin package worked correctly to send a temporary signed URL.

To my surprise, the test failed not because something in the code was wrong, but because the named route I registered couldn’t be found. It was something like this, oversimplified for illustration reasons:

The failing test that couldn’t find the named Route registered.

The above code failed on the assertion line. The Router couldn’t find the route I just created by its name, even if I registered just a line away.

After a quick debug tour by looking into the Route Collection through the Route::getRoutes() method confirmed it did exist, and was registered successfully, with its name.

Something was wrong. Was I wrong? Was the test wrong? Was Laravel wrong? As with all murder mysteries, there is always an explanation.

The lookup tables

Tracking back the code around routing, I found that just after the Router Service Provider receives all your pre-registered routes through their respective files, it will fire two methods that you may never heard about just now: refreshNameLookups() and refreshActionLookups().

A glance on the Route Service Provider of Laravel.

The first, as the name implies, will create an array of named routes pointing to the real Route instance. The second does the same, but using the (controller) action of each Route.

The purpose for these “lookup tables” is to allow the application to retrieve a Route by their name, or the action it points to. An array key lookup is miles faster than iterating through the whole Route Collection each time the app needs to find a route.

Why these exists in the first place? This is the important part. The registration is triggered only through one of the route methods, not before or after. Adding the name or action after one of these methods is called won’t register them in the lookup tables because the check was already done, so Laravel is forced to inspect the whole list to ensure they are added correctly for the convenience of the developer.

Fixing the problem of a Route not found

You may think the direct fix is relatively costly, and you’re right.

Both refreshNameLookups() and refreshActionLookups() will iterate through the whole list of Routes to compile their respective lookup tables. Calling them each time we want to register a Route is counterproductive and makes our application slower the more routes it has.

Instead, we can use a very simple trick to register a route on-demand: create the Route starting with the name and/or action, and just then the rest of the information about the Route itself.

Adding the name before triggering the registration.

This is not mandatory for the route files. We only need this to register a Route while the application is already booted, like during a Request or inside a test method.

In other words, adding the name and action in the Route instance before registering will allow Laravel to add them to the lookup tables immediately. Since the name and action will exist in the Route instance before the registration occurs, there is no need to invoke a lookup table refresh.

Back to the test for Laragear MailLogin, I can safely expect the name route to be found, since I can start with name() and then use get().

Since all tests passed, I published the package as the faster way to handle Mail Login in Laravel.

--

--

Italo Baeza Cabrera
Italo Baeza Cabrera

Written by Italo Baeza Cabrera

Graphic Designer graduate. Full Stack Web Developer. Retired Tech & Gaming Editor. https://italobc.com

No responses yet