Dev

Laravel: How to better organize your routes

Profil Picture

Guillaume Briday

2 minutes

By default, Laravel comes with four route files to manage the entry points of our application. These files handle routes for APIs, WebSockets, console commands, and, of course, web browsers. If the application is small or medium-sized, it's still relatively easy to manage.

However, things get complicated as the application grows. The files become very long, it's easy to get lost in the available groups, there may be duplicates, and there are often many conflicts to handle. Fortunately for us, in Laravel, nothing is set in stone, and we'll see how to better organize things.

How are route files declared?

The route files provided when creating a new project are simply an example of a configuration that Laravel suggests, and we can change it. In this case, I am primarily talking about the routes for APIs and the web.

The route files are called in a standard ServiceProvider located in the file app/Providers/RouteServiceProvider.php.

The map method is used to call protected methods, which simply declare routes as we would do directly in the files. For example, the routes/web.php file is essentially a route group that uses the default namespace and the web middleware.

/**
  * Define the "web" routes for the application.
  *
  * These routes all receive session state, CSRF protection, etc.
  *
  * @return void
  */
protected function mapWebRoutes()
{
    Route::middleware('web')
          ->namespace($this->namespace)
          ->group(base_path('routes/web.php'));
}

Declaring additional route files

Nothing prevents you from defining a new group of routes to manage a back office (for example), which is very often the case for many applications:

/**
  * Define the routes for the application.
  *
  * @return void
  */
public function map()
{
    $this->mapApiRoutes();

    $this->mapWebRoutes();

    $this->mapAdminRoutes();
}

/**
  * Define the "admin" routes for the application.
  *
  * These routes are typically stateless.
  *
  * @return void
  */
protected function mapAdminRoutes()
{
    Route::prefix('admin')
          ->middleware(['web', 'auth', 'role:admin'])
          ->namespace($this->namespace . '\Admin')
          ->as('admin.')
          ->group(base_path('routes/admin.php'));
}

In my case, I define a group of routes, for the web, that is accessible only to authenticated users with the admin role. Additionally, I change the namespace of the controllers to better isolate them from the rest of the application, and finally, I add a prefix to distinguish these routes.

Thus, all the routes in the admin.php file will already have all these properties without the need to redefine a group for that.

If you are developing a versioned API, you can also have a route file for each version like this:

└── routes
    └── api
        ├── v1.php
        └── v2.php

A more organized architecture with fewer conflicts

If we compare our route architecture before and now, we can see that we have significantly reduced visual clutter and avoided many conflicts since changes will be made in separate files.

Previously, we had something like this:

<?php
# routes/web.php

Route::prefix('admin')->middleware(['auth', 'role:admin'])->namespace('Admin')->as('admin.')->group(function () {
    Route::resource('photos', 'PhotoController');
    Route::resource('posts', 'PostsController');
    // Many more routes...
});

Route::view('/', 'home');
// Many more routes...

Route::middleware('auth')->group(function () {
    Route::resource('photos', 'PhotoController');
    Route::resource('posts', 'PostsController');
    // Many more routes...
});

And now, our files look like this:

<?php
# routes/web.php

Route::view('/', 'home');

// Many more routes...
<?php
# routes/admin.php

Route::resource('photos', 'PhotoController');
Route::resource('posts', 'PostsController');

// Many more routes...
<?php
# routes/auth.php

Route::resource('photos', 'PhotoController');
Route::resource('posts', 'PostsController');

// Many more routes...

Conclusion

I think it's best to avoid over-separating routes in smaller projects to keep things as simple as possible, and to migrate to this solution when the need arises.

You can find a concrete use case at guillaumebriday/laravel-blog.

Thank you!

Simplify your time tracking with Timecop

Timecop is a time tracking app that brings simplicity in your day to day life.

Timecop projects