Code Bright: Advanced Routing

← Back to Index

This title was written for Laravel version 4. If you're looking for material on the latest version of Laravel, then please check out Code Smart.

Oh I see, you’re back for more then. Basic routing just wasn’t good enough for you? A bit greedy are we? Well fear not Laravel adventurer, because I have some dessert for you.

In the Filters chapter you learned about how we can give an array as the second parameter to the routing methods to allow for more information to be included with our route definition. Like this:

<?php

// app/routes.php

Route::get('/', array(
    'before' => 'sexyfilter',
    function() {
        return View::make('hello');
    }
));

In this example, we are using the array syntax to include information about what filters we want to apply to the route. It doesn’t end there though, you can do a lot more with this array. Let’s take a look at what we have available.

Named Routes

URIs are fine and dandy. They sure help when it comes to giving structure to the site, but when you have a more complex site they can become a little long. You don’t really want to have to remember every single URI of your site, it will become boring fast.

Fortunately, Laravel has provided the named routing ability to alleviate some of this boredom. You see, we can give our routes a nickname, it looks a little like this:

<?php

// app/routes.php

Route::get('/my/long/calendar/route', array(
    'as' => 'calendar',
    function() {
        return View::make('calendar');
    }
));

Using the as index of our route array we can assign a nickname to a route. Try to keep it short, yet descriptive. You see, Laravel has a number of methods that help you generate links to the resources served by your application, and many of these have the ability to support route names. I’m not going to cover them all here, there’s a chapter coming up that will cover it all in detail, however here’s one simple example.

// app/views/example.blade.php

{{ route('calendar') }}

This simple helper will output the URL to the named route who’s nickname you pass to it. In this case it would return http://localhost/my/long/calendar/route. The curly braces are just to echo it out within a Blade template. You still remember Blade right? I hope so!

So how useful is this? Well, as I said before, you don’t have to remember long URLs anymore. Although, maybe you have a super brain. Remembering URLs might be trivial for you. Well, there’s another advantage I’d like to share.

Let’s imagine for a second that you had a number of views with links to a certain route. If the route links were entered manually, and you were to change the URL for the route, then you would also have to change all of the URLs. In a large application this could be an incredible waste of your time, and let’s face it, you’re a Laravel developer now. Your time is worth big money.

If we use the route() helper, and then decide to change our URL, we no longer need to modify all of the links. They will all be resolved by their nickname. I always try to name my routes if I can, it saves so much time later if you need to restructure.

Do you remember the Redirect response object? Well you can use the route method on it to redirect to a named route. For example:

<?php

return new Redirect::route('calendar');

Also, if you want to retrieve the nickname of the current route, you can use the handy currentRouteName() method on the 'Route’ class. Like this:

<?php

// app/routes.php

$current = Route::currentRouteName();

Be sure to remember that all of these advanced features are available to Controllers as well as routed Closures. To route to a Controller, simply add the uses parameter to the routing array, along with a controller-action pair.

<?php

// app/routes.php

Route::get('/my/long/calendar/route', array(
    'as' => 'calendar',
    'uses' => 'CalendarController@showCalendar'
));

Easy right? Now let’s look at how we can make our routes more secure.

Secure Routes

You may want your routes to respond to secure HTTP URLs so that they can handle confidential data. HTTPS URLs are layered on top of the SSL or TLS protocol to allow for increased security when you need it. Here’s how you can allow your routes to match this protocol.

<?php

// app/routes.php

Route::get('secret/content', array(
    'https',
    function () {
        return 'Secret squirrel!';
    }
));

By adding the HTTPS index to our routing array, our route will now respond to requests made to the route using the HTTPS protocol.

Parameter Constraints

In the basic routing chapter we discovered how we could use parameters from our URL structure within our application logic. For a routed Closure it looks like this:

<?php

// app/routes.php

Route::get('save/{princess}', function($princess)
{
    return "Sorry, {$princess} is in another castle. :(";
});

Well, I for one have never heard of a princess called ‘!1337f15h’. It sounds a lot more like a Counterstrike player to me. We don’t really want our route to respond to fake princesses, so why don’t we try and validate our parameter to make sure that it consists of letters only.

Let’s lead with an example of this in action.

<?php

// app/routes.php

Route::get('save/{princess}', function($princess)
{
    return "Sorry, {$princess} is in another castle. :(";
})->where('princess', '[A-Za-z]+');

In the above example, we chain an additional where() method onto the end of our route definition. The ‘where’ method accepts the placeholder name as the first parameter, and a regular expression as the second.

Now I’m not going to cover regular expressions in detail. The topic is vast, no really, it’s incredibly vast. It could be a complete book of its own. Simply put, the regular expression above ensures that the princess’ name must be made up of either capital or lowercase letters, and must have at least one letter.

If the parameter doesn’t satisfy the regular expression that we have provided, then the route won’t be matched. The router will continue to attempt to match the other routes in the collection.

You can attach as many conditions to your route as you like. Take a look at this for example:

<?php

// app/routes.php

Route::get('save/{princess}/{unicorn}', function($princess, $unicorn)
{
    return "{$princess} loves {$unicorn}";
})->where('princess', '[A-Za-z]+')
  ->where('unicorn', '[0-9]+');

The unicorn parameter has been validated against one or more numbers, because as we know, unicorns always have numerical names. Just like my good friend 3240012.

Route Groups

Remember how we were able to provide conditions to our routes in the Filters chapter? That was really handy right? It would be a shame to have to attach the same filter to many route definitions though.

Wouldn’t it be great if we could encapsulate our routes, and apply a filter to the container? Well, you might have guessed already, but here’s an example that does exactly that.

<?php

// app/routes.php

Route::group(array('before' => 'onlybrogrammers'), function()
{

    // First Route
    Route::get('/first', function() {
        return 'Dude!';
    });

    // Second Route
    Route::get('/second', function() {
        return 'Duuuuude!';
    });

    // Third Route
    Route::get('/third', function() {
        return 'Come at me bro.';
    });

});

In the above example we are using the group() method on the ‘Route’ object. The first parameter is an array. It works just like the ones we have been using within our routing methods. It can accept filters, secure indexes, and many of the other routing filters. The second parameter should be a Closure.

When you define additional routes within this Closure, the routes inherit the properties of the group. The three routes within the group above are all protected by the ‘onlybrogrammers’ before filter.

Now, we can use the routing array filters we discovered earlier in the chapter on the groups, but we can also use some new features that are specific to route groups. Let’s take a look at these new features.

Route Prefixing

If many of your routes share a common URL structure, you could use a route prefix to avoid a small amount of repetition.

Take a look at the following example.

<?php

// app/routes.php

Route::group(array('prefix' => 'books'), function()
{

    // First Route
    Route::get('/first', function() {
        return 'The Colour of Magic';
    });

    // Second Route
    Route::get('/second', function() {
        return 'Reaper Man';
    });

    // Third Route
    Route::get('/third', function() {
        return 'Lords and Ladies';
    });

});

Using the prefix array option of the route group, we can specify a prefix for all of the URIs defined within the group. For example, the three routes above are now accessible at the following URLs.

/books/first

/books/second

/books/third

Use route prefixes to avoid repetition within your routes, and to group them by purpose for organisational or structural value.

Domain Routing

URI’s are not the only way to differentiate a route. The host can also change. For example, the following URLs can reference different resources.

http://myapp.dev/my/route

http://another.myapp.dev/my/route

http://third.myapp.dev/my/route

In the above examples you can see that the subdomain is different. Let’s discover how we can use domain routing to serve different content from different domains.

Here’s an example of domain based routing:

<?php

// app/routes.php

Route::group(array('domain' => 'myapp.dev'), function()
{
    Route::get('my/route', function() {
        return 'Hello from myapp.dev!';
    });
});

Route::group(array('domain' => 'another.myapp.dev'), function()
{
    Route::get('my/route', function() {
        return 'Hello from another.myapp.dev!';
    });
});

Route::group(array('domain' => 'third.myapp.dev'), function()
{
    Route::get('my/route', function() {
        return 'Hello from third.myapp.dev!';
    });
});

By attaching the ‘domain’ index to the route grouping array, we are able to provide a host name, that must match the current hostname for any of the routes inside to be executed.

The host name can either be a subdomain, or a completely different subdomain. As long as the web server is configured to serve requests from each host to Laravel, then it will be able to match them.

That’s not all there is to domain based routing. We can also capture portions of the host name to use as parameters, just as we did with URI based routing. Here’s an example of this in action.

<?php

// app/routes.php

Route::group(array('domain' => '{user}.myapp.dev'), function()
{
    Route::get('profile/{page}', function($user, $page) {
        // ...
    });
});

You can provide a placeholder for a domain parameter within the ‘domain’ index by using { curly braces }, just like our URI parameters. The value of the parameter will be passed before any parameters of the routes held within the group.

For example, if we were to visit the URL:

http://taylor.myapp.dev/profile/avatar

Then the first value $user that is passed to the inner Closure, would be ‘taylor’, and the second value $page would be ‘avatar’.

By using a combination of wildcard subdomains, and routing parameters, you could prefix the domain with the username of your application’s users.

My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.

It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!