Laravel - Using (:any?) wildcard for ALL routes?

PhpLaravelRoutesWildcardLaravel 3

Php Problem Overview


I am having a bit of trouble with the routing.

I'm working on a CMS, and I need two primary routes. /admin and /(:any). The admin controller is used for the route /admin, and the view controller should be used for anything else than /admin. From the view controller, I will then parse the url and show the correct content.

This is what I have:

Route::get(array('admin', 'admin/dashboard'), array('as' => 'admin', 'uses' =>'admin.dashboard@index'));
Route::any('(:any)', 'view@index');

The first route works, but the second one doesn't. I played around with it a little bit, and it seems if I use (:any) without the question mark, it only works if I put something after /. If i do put the question mark there, it doesn't work at all.

I want all of the following routes to go to view@index:

/
/something
/something/something
/something/something/something
/something/something/something/something
...etc...

Is this possible without hardcoding a bunch of (:any?)/(:any?)/(:any?)/(:any?) (which I don't even know works)?

What's the best way to go about this?

Php Solutions


Solution 1 - Php

Laravel 5

This solution works fine on Laravel 5:

Route::get('/admin', function () {
  
  // url /admin
  
});

Route::get('/{any}', function ($any) {
  
  // any other url, subfolders also

})->where('any', '.*');

Lumen 5

This is for Lumen instead:

$app->get('/admin', function () use ($app) {
  //
});

$app->get('/{any:.*}', function ($any) use ($app) {
  //
});

Solution 2 - Php

Hitting a 404 status seems a bit wrong to me. This can get you in all kind of problems when logging the 404's. I recently bumped into the same wildcard routing problem in Laravel 4 and solved it with the following snippet:

Route::any('{slug}', function($slug)
{
    //do whatever you want with the slug
})->where('slug', '([A-z\d-\/_.]+)?');

This should solve your problem in a controlled way. The regular expression can be simplified to:

'(.*)?'

But you should use this at your own risk.

Edit (addition):

As this overwrites a lot of routes, you should consider wrapping it in an "App::before" statement:

    App::before(function($request) {
            //put your routes here
    });

This way, it will not overwrite custom routes you define later on.

Solution 3 - Php

Edit: There has been some confusion since the release of Laravel 4 regarding this topic, this answer was targeting Laravel 3.

There are a few ways to approach this.

The first method is matching (:any)/(:all?):

Route::any('(:any)/(:all?)', function($first, $rest=''){
    $page = $rest ? "{$first}/{$rest}" : $first;
    dd($page);
});

Not the best solution because it gets broken into multiple parameters, and for some reason (:all) doesn't work by itself (bug?)

The second solution is to use a regular expression, this is a better way then above in my opinion.

Route::any( '(.*)', function( $page ){
    dd($page);
});

There is one more method, which would let you check if there are cms pages even when the route may have matched other patterns, provided those routes returned a 404. This method modifies the event listener defined in routes.php:

Event::listen('404', function() {
    $page = URI::current();
    // custom logic, else
    return Response::error('404');
});

However, my preferred method is #2. I hope this helps. Whatever you do, make sure you define all your other routes above these catch all routes, any routes defined after will never trigger.

Solution 4 - Php

Route::get("{path}", "SomeController@serve")->where('path', '.+');

The above code will capture the recursive sub urls you mentioned:

/
/something
/something/something
/something/something/something
/something/something/something/something

Any other special cases, such as admin/*, you can capture before this one.

Solution 5 - Php

Just spelling-out my experience in case it helps someone piece something together.

I built a self-API-consuming React app on Laravel. It has a single view served by Laravel/Lumen. It uses the React router. Clicking links in the app always worked, but typing-in URLs needed the following consideration:

In Laravel I used the following in my web.php routes file:

Route::view('/{path?}', 'app')
    ->where('path', '.*')
    ->name('react');

And everything worked.

Then I switched the project to Lumen. Thanks to this post, I used the following in my web.php routes file:

$router->get('/{all:.*}', function() {
    return view('app');
});

This worked for first level URLS such as:

/
/something 

However,

/something/something etc.

did not.

I looked in the network tab in Google Developer tools and noticed that the URL for app.js was appending /something in front of app.js on second and higher tier URLS, such as:

myapp.com/something
app.js URL:  myapp.com/js/app.js  (as it should be)

myapp.com/something/something
app.js URL:  myapp.com/something/js/app.js  (not found)

All I had to do was add a leading slash to my app.js source in my single view page such as:

<script src="/js/app.js" defer></script>

Instead of:

<script src="js/app.js" defer></script>

so:

This worked in Laravel (It was a Blade file that may have automatically resolved the js/app.js URL)

<script src="{{ asset('js/app.js') }}" defer></script>

with

Route::view('/{path?}', 'app')
    ->where('path', '.*')
    ->name('react');

But, I had to do this in Lumen (Not a Blade file):

<script src="/js/app.js" defer></script>

with

$router->get('/{all:.*}', function() {
    return view('app');
});

Solution 6 - Php

Add this in the end of routes file

App::missing(function($exception)
{
	return View::make('notfound');
});

from http://scotch.io/tutorials/simple-and-easy-laravel-routing

Solution 7 - Php

Laravel 8 / redirect only under subdirectory

I wanted to redirect not all non-existing URLs, but only the ones in a specific subdirectory (like https://example.com/subdir/*)

If I just had wanted to redirect all missing URLs I could have just used the fallback route at the end of the web.php file:

Route::fallback(function () {
    //
});

But as I wanted to redirect only urls in subdirectories, I used this code, which worked for me (just redirecting to /):

Route::get('subdirectory/{any}', function($page){return redirect()->to('/');})->where('any', '.*');

I found this in the FallbackRouteTest.php.

Solution 8 - Php

Thanks for the solution William. However methods 1 & 2 aren't working anymore Laravel 4, and in order to use solution #3 in Laravel 4 you will have to fire the 404 event in your start/global.php file.

App::error(function(Exception $exception, $code)
{
    // i.o. -> this is our catchall!
    // http://stackoverflow.com/questions/13297278/laravel-using-any-wildcard-for-all-routes
    Event::fire('404');

    return View::make('error')->with('exception', $exception)->with('code', $code);

    Log::error($exception);
});

Now we can handle this in our routes.php file:

Event::listen('404', function() {
    // url?
    $url = Request::path();

    // LOGIC HERE

    // else
    return View::make('error');
});

Solution 9 - Php

Having basic lumen scaffolding. In my case, I have 2 frontend apps and api routes

<?php // routes/web.php
/** @var \Laravel\Lumen\Routing\Router $router */

$router->group([
    'prefix' => '/api/v1',
    'namespace' => 'App\Http\Controllers'
], function () use ($router) {

    require 'routes-group1.php';
    require 'routes-group2.php';
    // ...
});

$router->get('/admin{any:.*}', function () {
    return file_get_contents('../public/a/index.html');
});

$router->get('{any:.*}', function () {
    return file_get_contents('../public/m/index.html');
});

Solution 10 - Php

Fallback Routes Since Laravel 5.5

Using the Route::fallback method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you would typically define the fallback route within your routes/web.php file, all middleware in the web middleware group will apply to the route. You are free to add additional middleware to this route as needed:

Route::fallback(function () {
    //
});

Note: The fallback route should always be the last route registered by your application.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionqwertyView Question on Stackoverflow
Solution 1 - PhpAndreaView Answer on Stackoverflow
Solution 2 - PhpEelke van den BosView Answer on Stackoverflow
Solution 3 - PhpWilliam Cahill-ManleyView Answer on Stackoverflow
Solution 4 - PhpGuyView Answer on Stackoverflow
Solution 5 - PhpLukeView Answer on Stackoverflow
Solution 6 - PhpEdmhsView Answer on Stackoverflow
Solution 7 - PhpAlexander TaubenkorbView Answer on Stackoverflow
Solution 8 - PhpChris SchalenborghView Answer on Stackoverflow
Solution 9 - PhpiamandrewlucaView Answer on Stackoverflow
Solution 10 - PhpMrEduarView Answer on Stackoverflow