Search code examples
phplaravellaravel-5laravel-5.4laravel-middleware

Apply Middleware to all routes except `setup/*` in Laravel 5.4


I'm experimenting with Middleware in my Laravel application. I currently have it set up to run on every route for an authenticated user, however, I want it to ignore any requests that begin with the setup URI.

Here is what my CheckOnboarding middleware method looks like:

public function handle($request, Closure $next)
{
    /** 
    * Check to see if the user has completed the onboarding, if not redirect.
    * Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
    */
    if ($request->user()->onboarding_complete == false && $request->path() != 'setup') {
        return redirect('setup');
    } else {
        return $next($request);
    }
}

This is being used in my routes like this:

Route::group(['middleware' => ['auth','checkOnboarding']], function () {
    Route::get('/home', 'HomeController@index');
    Route::get('/account', 'AccountController@index');

    Route::group(['prefix' => 'setup'], function () {
        Route::get('/', 'OnboardingController@index')->name('setup');
        Route::post('/settings', 'SettingsController@store');
    }); 
});

Now, if I go to /home or /account I get redirected to /setup as you would expect. This originally caused a redirect loop error hence why & $request->path() != 'setup' is in the Middleware.

I feel like this is a really clunky way of doing it, and obviously doesn't match anything after setup like the setup/settings route I have created.

Is there a better way to have this Middleware run on all routes for a user, but also set certain routes that should be exempt from this check?


Solution

  • There's nothing wrong with what you're doing, however, I would suggest splitting your route groups up instead i.e.:

    Route::group(['middleware' => ['auth', 'checkOnboarding']], function () {
        Route::get('/home', 'HomeController@index');
        Route::get('/account', 'AccountController@index');
    });
    
    Route::group(['prefix' => 'setup', 'middleware' => 'auth'], function () {
        Route::get('/', 'OnboardingController@index')->name('setup');
        Route::post('/settings', 'SettingsController@store');
    });
    

    Alternatively, have a parent group for your auth:

    Route::group(['middleware' => 'auth'], function () {
    
        Route::group(['middleware' => 'checkOnboarding'], function () {
            Route::get('/home', 'HomeController@index');
            Route::get('/account', 'AccountController@index');
        });
    
        Route::group(['prefix' => 'setup'], function () {
            Route::get('/', 'OnboardingController@index')->name('setup');
            Route::post('/settings', 'SettingsController@store');
        });
    });
    

    This will also mean you can remove the extra condition in your middleware:

    /**
     * Check to see if the user has completed the onboarding, if not redirect.
     * Also checks that the requested URI isn't the setup route to ensure there isn't a redirect loop.
     */
    return $request->user()->onboarding_complete ? $next($request) : redirect('setup');
    

    Hope this helps!