Organising routes in Laravel 4

Sorry that it took me so long to write this, I've been up to a lot recently and completely lost track of time.


A few weekends back I tweeted a screenshot of how I lay out my routes in any of my Laravel projects. It somewhat exploded due to a retweet by the @laravelphp account, some loved it, some hated it.

This is a brief post on how and why I lay out my routes as I do. It'll also contain more information than the original screenshot than what my tweet showed.

Why not just whack all routes in one file?

You're crazy!

When you have a website which is made up of lots of routes (not all of which need to be dynamic) your routes.php file becomes big and bloated. Bloat always leads to overcomplicated design choices and regret down the line. It's not good.

Start with the structure

I find that the easiest way to manage different parts of my website/application is to break it down into directories. First things first though, we need a directory to store our routes in. Let's walk through that now.

First we create a routes folder within the app directory of our application. This is where all of our route files will be stored.

Now that we have this directory we need to make sure that the files within it are going to be auto-loaded. To do this we need to add it into composer.json:

"autoload": {
    "classmap": [
        "app/commands",
        "app/controllers",
        "app/models",
        "app/database/migrations",
        "app/database/seeds",
        "app/tests/TestCase.php",
        "app/routes"
    ]
}

Now when you run composer du or php artisan du it'll make sure that all of the files within that directory are loaded. We can also add it app/start/global.php, add the directory in the ClassLoader::addDirectories section - just duplicate one of the lines and change the directory to routes.

Once we have Laravel loading our route files, we can split up the routes into sub-directories or individual route files. For me, this depends on how complicated my app is.

Bonus! You can move your routes around at a later date, they're not tied to the directory or route file that you put them into now.

If there are several parts to your application - for example; customer facing and the administration panel, I'll split the routes up into:

  • web
  • manage

At any rate, I always create an app.php file within every route directory which contains the top-level routes (think index, about, etc) and then split down more complicated routes such as contact.php or payments.php etc. The filename doesn't matter, but it's good to be descriptive so that you can easily navigate the structure in the future.

Sometimes there may be routes which need to be shared - for example, we have an application which is split into sub-domains that all share a common user/profile route - for this we can create separate files within the routes directory itself.

If you're unsure, this is an example routes directory:

  • routes:
    • user.php - routes in here will be shared across all of our domains.
    • admin:
      • app.php
      • products.php
      • users.php
    • web:
      • app.php - notice that this file has already been called? It's fine!
      • contact.php
      • payments.php
      • mailinglist.php

Loading the routes

Here is the fun bit which brings it altogether. In the existing routes.php file we replace the routes with:

// Recursively load all of the routes within the app/routes/
routesInDirectory();

function routesInDirectory($app = '') {  
    $routeDir = app_path('routes/' . $app . ($app !== '' ? '/' : NULL));
    $iterator = new RecursiveDirectoryIterator($routeDir);
    $iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);

    foreach ($iterator as $route) {
        $isDotFile = strpos($route->getFilename(), '.') === 0;

        if (!$isDotFile && !$route->isDir()) {
            require $routeDir . $route->getFilename();
        }
    }
}

By default the routesInDirectory function will load all of the routes within routes/ which is fine, but how do we split the routes up by, say a, domain? I always set the management panel onto a sub-domain so that it's harder to exploit. In our internal application we split up the "apps" by sub-domain.

Introducing routers

We have our directory, we have the routes loading, but we now need to split the routes by domain. How? Easy!

// Let's create a router for the app/routes/web directory
$routerWeb = function() {
    routesInDirectory('web');
};

// Any routes on *this* domain should use the web routes
Route::group(['domain' => 'smashing-application.co.uk'], $routerWeb);  

The reason we create an anonymous function here is so that we can load the same routes for smashing-application.co.uk and smashing-application.co.uk.dev - it makes it much easier for us. Alternatively replace the $routerWeb call with the function itself. It's fine.

If we want to add our admin sub-domain, it's the same thing as before:

// Check out the anonymous function usage.
Route::group(['domain' => 'admin.smashing-application.co.uk'], function() {  
    routesInDirectory('admin');
});

And there we have it. A well laid out, dynamically loaded route setup.


comments powered by Disqus