UPDATED (Laravel) Auto Generate Named Routes For Easy Reverse Routing

UPDATED: It occured to me that having the route names match the controller name might not lend itself well to preventing the need to update multiple places in the event of a controller’s name changing. I have updated this post with a better method to tackle that.

What *Is* Reverse Routing?
Reverse routing is an awesome feature. In Laravel (as well as other frameworks) when you create a route you can also define a name for it. Then, when you need to generate a link in your app, instead of doing your standard

<a href="/blah/bler/blo">Link</a>

You can do this:

<a href="<?php echo URL::to_route('some_route', array('bler', 'blo')); ?>">Link</a>

The difference, you ask? Let’s say later you decide to rename the Blah controller to Clover. You’d have to go through your entire app and replace any links starting with “blah” with “clover”.

With reverse routing, you’d make exactly one replacement: in the routes.php file. Any links in your app using the second example will automatically go to the clover controller. Yay!

So How Do I Do The Magic?

As you can tell, I very much like using reverse routing. However, I realized that Laravel does not automatically generate names for the routes created by Route/r::controller (which is not a critical flaw; not everyone has the need for it, and that’s fine). After a bit of digging and head-scratching I came up with a fairly easy way to have it do the work of naming all routes for me.

NOTE: This requires manually setting your controller names instead of using Controller::detect() (probably not a bad idea anyway since it’s less file lookups for Laravel to do)

1. Open routes.php and list the controllers for the bundle. If you want to specify a name, do a key => val pair. Example:
Route::controller(array(
// This will set the admin/home controller as a route named foo
'foo' => 'admin.home',
// This will be a route called users, since we did not set a name
'users'
));

2. Create 2 files in your application folder. I placed mine in application/libraries. route.php and router.php.

3. Open up router.php and put the following into it:

<?php

class Router extends Laravel\Routing\Router {

/**
* Register a controller with the router.
*
* @param string|array $controller
* @param string|array $defaults
* @param bool $https
* @return void
*/
public static function controller($controllers, $defaults = 'index', $https = null)
{
foreach ((array) $controllers as $as => $identifier)
{
list($bundle, $controller) = Bundle::parse($identifier);

// First we need to replace the dots with slashes in thte controller name
// so that it is in directory format. The dots allow the developer to use
// a cleaner syntax when specifying the controller. We will also grab the
// root URI for the controller's bundle.

///// Here is where the named route will be set. If the $as variable is numeric,
//// we will use the controller name
(is_numeric($as)) and $as = $controller;
$controller = str_replace('.', '/', $controller);
$root = Bundle::option($bundle, 'handles');

// If the controller is a "home" controller, we'll need to also build a
// index method route for the controller. We'll remove "home" from the
// route root and setup a route to point to the index method.
if (ends_with($controller, 'home'))
{
static::root($identifier, $controller, $root);
}

// The number of method arguments allowed for a controller is set by a
// "segments" constant on this class which allows for the developer to
// increase or decrease the limit on method arguments.
$wildcards = static::repeat('(:any?)', static::$segments);

// Once we have the path and root URI we can build a simple route for
// the controller that should handle a conventional controller route
// setup of controller/method/segment/segment, etc.
$pattern = trim("{$root}/{$controller}/{$wildcards}", '/');

// Finally we can build the "uses" clause and the attributes for the
// controller route and register it with the router with a wildcard
// method so it is available on every request method.
$uses = "{$identifier}@(:1)";
///// Just adding the as variable to the compact statement so it'll get set upon register
$attributes = compact('uses', 'defaults', 'https', 'as');

static::register('*', $pattern, $attributes);
}
}

}

Here we are basically adding a couple of lines so the named route (defined by the ‘as’ attribute) will be created for us when Laravel is detecting controllers & generating routes.

4. Open up your route.php file and add the following:

<?php

class Route extends Laravel\Routing\Route {
/**
* Register a controller with the router.
*
* @param string|array $controllers
* @param string|array $defaults
* @return void
*/
public static function controller($controllers, $defaults = 'index')
{
Router::controller($controllers, $defaults);
}

/**
* Register a secure controller with the router.
*
* @param string|array $controllers
* @param string|array $defaults
* @return void
*/
public static function secure_controller($controllers, $defaults = 'index')
{
Router::controller($controllers, $defaults, true);
}
}

The reason we need to extend the route.php file as well is that the default file (found in laravel/routing/route.php) points to laraveroutingrouter, which means our custom router class would never get used. So, we simply override the controller functions and add the handy backslash in front of the Router call so it will look for our router file.

5. Open up application/config/application.php and comment out Route and Router in the aliases section.

You should now be have auto-generated named routes. The best part is that any custom route names you define will not be overridden by this method.

About these ads
Leave a comment

3 Comments

  1. Hey Nerdy Mom! I just discovered your blog and I really like the information and the attitude. I have immediately developed deep respect for you!

    Quick question:
    I see you’ve worked with CakePHP and CodeIgniter, and now you’re mentioning Laravel. I’ve been happily using CodeIgniter for a while now, but lately I see a lot of people mentioning (and loving) Laravel. I’m not looking for one of those “which is best” answers. Long ago, I learned that this is not a good way to look at various frameworks. But I’m curious — what are your general impressions when comparing these three frameworks?

    OK, maybe that wasn’t a quick question really. You can make it fair and give me a “quick answer.”

    Glad to discover your blog. Will be checking in regularly.

    Matthew

    Reply
  2. nerdmom

     /  August 2, 2012

    Matthew, after some thought I decided your question deserved its own post Enjoy! :)

    Reply
  1. Comparing Laravel, CodeIgniter, & CakePHP « A Nerdy Mom!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 136 other followers

%d bloggers like this: