Routing in Kohana 3

Exclusive offer: get 50% off this eBook here
Kohana 3.0 Beginner’s Guide

Kohana 3.0 Beginner’s Guide — Save 50%

Develop professional web applications with Kohana.

$26.99    $13.50
by Jason D. Straughan | September 2011 | Open Source

In this article by Jason D. Straughan, author of Kohana 3.0 Beginner’s Guide, we will take a look at how we can use routes to keep our users, and data, flowing to the proper controller actions, and how to build dynamic applications using Kohana’s routing abilities.

Specifically, we will cover:

  • Routing in Kohana
  • How to use Routing in our applications
  • Advanced routing techniques

 

(For more resources on this topic, see here.)

The reader can benefit from the previous article on Request Flow in Kohana 3.

Routing in Kohana

If you remember, the bootstrap file comes preconfigured with a default route that follows a very simple structure:

Route::set(‘default’, ‘(<controller>(/<action>(/<id>)))’)
->defaults(array(
‘controller’ => ‘welcome’,
‘action’ => ‘index’,
));

This tells Kohana that when it parses the URL for any request, it first finds the base_url, and then the next segment will contain the controller, then the action, then an ID. These are all optional setgments, with the default controller and action being set in the array. We have taken advantage of this route with other controllers like our Profile and Message controller.

When we visit http://localhost/egotist/profile, the route sets the controller to profile, and since no action or ID is explicitly defined in the URL, the default action of ‘index’ is used. When we requested http://localhost/egotist/messages/get_messages from within our Profile Controller, we also followed this route; however, neither defaults were needed, and the route asked for the Messages Controller and its get_messages action.

In our Profile controller, we are only using one array of example messages to test functionality and the expected behavior of our application. When we implement a data store and have multiple users with profiles in our application, we will need a way to decipher which profile a user wants to see.

Because the default route already has an available parameter for ID, we can use that to pass an ID to our Profile Controller’s index action, and have the messages controller then find the proper messages for that user.

 

Time for action – Making profiles dynamic using ID

Once a database is tied to our application, and more than one user has a profile, we will need some way of knowing which profile to display. A simple and effective way to do this is to pass a user ID in the route, and have our controller use that ID to find the right messages for the right user. Let’s add some more test data to our messages system, and use an ID to display the right messages.

  1. Open the Profile Controller in our application/classes/controller/ directory named profile.php. Since the action_index() method is the controller action that is called when a profile is viewed, we will need to edit it to look for the ID parameter in the URI like this:

    public function action_index()
    {
    $content = View::factory(<profile/public>)
    ->set(<username>, <Test User>)
    ->bind(<messages>, $messages);
    $id = (int) $this->request->param(‘id’);
    $messages_uri = "messages/get_messages/$id";
    $messages = Request::factory($messages_uri)->execute()-
    >response;
    $this->template->content = $content;
    }

  2. Now, we are retrieving the ID from the route and passing it along in our request to the Messages Controller. This means that class must also be updated. Open the messages.php file located in application/classes/controllers/ and modify its action_get_messages() method as follows:

    public function action_get_messages()
    {
    $id = (int) $this->request->param(‘id’);
    $messages = array(
    1 => array(
    ‘This is test message one for user 1’,
    ‘This is test message two for user 1’,
    ‘This is test message three for user 1’
    ),
    2 => array(
    ‘This is test message one for user 2’,
    ‘This is test message two for user 2’,
    ‘This is test message three for user 2’
    )
    );
    $messages = array_key_exists($id, $messages) ? $messages[$id] :
    NULL;
    $this->request->response = View::factory(‘profile/messages’)
    ->set(‘messages’, $messages);
    }

  3. Open the page http://localhost/egotist/profile/index/2/. It should look like this:

    Kohana 3.0,Beginner's Guide

    Browsing to http://localhost/egotist/profile/index/1/ will show the messages for user 1, i.e., the test messages placed in the message array under key 1.

What just happened?

At the very beginning of our index action in our Profile Controller, we set our $id variable by getting the ID parameter from the route. Since Kohana has parsed our route for us, we can now access these parameters via the request object’s param() method.

Once we got the ID variable, we then created and executed the request for the message controller’s get_messages action, and passed the ID to that method for it to use.

In the Message Controller, we used the same method to extract the ID from the request, and then used that ID to determine which messages from the messages array to display. Although this works fine for illustrating routing for these two users, the code is far from ready, even without a data store or real user data, but it does show how the parameters can be read and used.

Because most of the functionality in the controller will be replaced with our database and more precise data being passed around, we can overlook the incompleteness of the current controller actions, and begin looking at creating a URL that is better looking than http://localhost/egotist/profile/index/2/ for finding a user profile by ID.

Creating friendly URLs using custom routes

Consider how nice it would be if our users could browse to a profile without putting ‘index’ in the action portion of the URI, like this: http://localhost/egotist/profile/2. This looks much more pleasing, and is more in line with what we would like our URLs to look like in web apps. It is in fact very easy to have Kohana use a route to remove the index action from the URI.

Routes not only make our URLs more pleasing and descriptive, but they make our application easier to maintain in the long run. We have more control over where our users are being directed from how the URL is constructed, without having to create controller actions designed to handle routing.

 

Time for action – Creating a Custom Route

So far, we have been using the default route that is in our application bootstrap. As our application grows, so will the number of available ‘starting points’ for our user’s requests. Not every controller, action, or parameter has to comply with the default route, and this gives us a lot of flexibility and freedom.

We can add a custom route to handle user’s profiles by adding it to our bootstrap.php file.

  1. Open the bootstrap.php file located in application/directory and modify the routes block so it looks like this:

    /**
    * Set the routes.Each route must have a minimum of a name,a URI
    * and a set of defaults for the URI.
    */

    Route::set(‘profile’, ‘profile/<id>’)
    ->defaults(array(
    ‘controller’ => ‘profile’,
    ‘action’ => ‘index’,
    ));
    Route::set(‘default’, ‘(<controller>(/<action>(/<id>)))’)
    ->defaults(array(
    ‘controller’ => ‘welcome’,
    ‘action’ => ‘index’,
    ));

  2. Now, we can view the profile pages without having to pass the index action in the URL. Open http://localhost/egotist/profile/2 in a browser; it should look like this:

    Kohana 3.0,Beginner's Guide

  3. Browsing to profiles with a more friendly URL is made possible through Kohana’s routes.

What just happened?

By setting routes using the Route::set static method, we are essentially creating filters that will be used to match requests with routes. We can name these routes; in this case we have one named default, and one named profile. Kohana uses the second parameter in the set() method to compare against the requested URI, and will call the first route that matches the request. Because it uses the first route that matches the request, it is very important when ordering route definitions. If we put the default route before the profile route, the profile route will never be used, as the default route would always match first.

Because it looks for a match, it does not use discretion when determining the right route for a request. So if we browse to http://localhost/egotist/profile/index/2, we will be directed to the default route, and get the same result. The default route may not be available for all the routes we create in the future, so create routes that are as explicit as we can for our needs.

Right now, our application assumes any data that is passed after a controller segment named ‘profile’ must be the ID for which we are looking. In our current application setup, we only need digits. If a user passes data into the URL that is not numeric for the ID parameter, we do not want it to go to that route. This can be accomplished easily inside the Route::set() method.

 

Kohana 3.0 Beginner’s Guide Develop professional web applications with Kohana.
Published: August 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

(For more resources on this topic, see here.)

 

Making routes explicit

Having routes that are properly defined means having routes that are explicit so they match only the request to which they are designed. Kohana::set() offers a third argument that accepts an array of URI segment keys and regular expression values. The URI segment keys are enclosed in angle brackets (<...>). The method then applies these regex filters to the segments when determining if there is a match.

 

Time for action – Adding regex to a route

Modifying our profile route with a regular expression will allow Kohana to send requests only to that route where the ID is a number.

Open our bootstrap.php file, located in application/directory, and modify the route’s set method for the profile route so it looks like this:

Route::set(‘profile’, ‘profile/<id>’, array(‘id’ => ‘[0-9]+’))
->defaults(array(
‘controller’ => ‘profile’,
‘action’ => ‘index’,
));

What just happened?

The regular expression [0-9]+ will only match one or more numeric characters, no matter how long. This works fine for requiring that the route only match requests where the URI has ‘profile’ in the first segment, and a numerical value in the second segment.

While this is great for finding a user by their ID, and the URL that a user would use to find the profile is nicer than it was before, it is still not as descriptive as it could be. It would be nice if the user’s name were also allowed to be passed into the path, even if the application had no use for it, it would add more description to the possible destination.

 

Allowing additional data in a URI

Apart from being easier to read and type, friendly URLs can also help to describe the content on the page for users and search engines. It is not uncommon to pass additional data in the route to better describe the page, even though it is optional and not used by the application itself.

Unfortunately, if we wanted to link to a profile with the user’s name appended to the URI, it would not find the route we have defined. If we linked to http://localhost/egotist/profile/2/some-users-name, we would get an application error, as there is not a route that is defined that matches this scheme, and the default route will look for a method named action_2 in the profile controller, as defined in the default route in our bootstrap file.

 

Time for action – Allowing additional segments in a route

By making a simple modification to our profile route, we can match any URI that has the basic requirements for our route, and allow some optional data to be passed. Although we will ignore this data in our controllers, other places, like search engines, may find it very useful.

Open the bootstrap.php file located in application/directory and modify the route’s set method for the profile route so it looks like this:

Route::set(‘profile’, ‘profile/<id>(/<optional>)’, array(
‘id’ => ‘[0-9]+’,
‘optional’ => ‘.*’)
)->defaults(array(
‘controller’ => ‘profile’,
‘action’ => ‘index’,
));

What just happened?

By adding the third segment to our URI structure for the profile route, we are allowing additional data to be passed after the ID, and still match the route we are wanting. If you notice, we also added another regular expression that will match any character, 0 or more times, to the third argument’s array, with a key referencing the new optional segment. Segments are defined as optional when they are enclosed in a set of parenthesis.

Without this regular expression, only one additional segment is allowed, but with the regex, we can now pass all the additional segments of data we wish, and still match this route. A quick glance at http://localhost/egotist/profile/2/some-user/more-data/even-more-stuff still shows our profile page for user number 2:

Kohana 3.0,Beginner's Guide

 

Using subdirectories with routes

The controller directory can quickly become overrun with files, making an otherwise well organized project become cluttered and hard to navigate. Besides being able to set default controllers and actions for a route, we can also define the directory that a route’s controllers live in. This can make organizing our files much easier.

 

Time for action – Using Subdirectories and Routes

We can assume there are going to be more than the five files in our classes/controller directory as our application grows. By organizing our controller classes in subdirectories, and then using our routes to define the location of our classes, we can better organize our application and prepare for future growth.

We can add a directory to our route, and then update our controller’s class declarations to better organize our files.

  1. Open the bootstrap.php file located in application/directory, and modify our profile route so it looks like this, adding the user-messages route below our profile route:

    Route::set(‘profile’, ‘profile/<id>(/<optional>)’, array(
    <id> => <[0-9]+>,
    <optional> => <.*>))
    ->defaults(array(
    ‘ directory’ => ‘user’,
    ‘controller’ => ‘profile’,
    <action> => <index>,
    ));

    Route::set(‘user-messages’,‘messages/<action>/<id>(/<optional>)’,
    array(
    ‘id’ => ‘[0-9]+’,
    ‘optional’ => ‘.*’))
    ->defaults(array(
    ‘directory’ => ‘user’,
    ‘controller’ => ‘messages’,
    ‘action’ => ‘index’,
    ));

  2. Next, we will need to create the user subdirectory inside our application/classes/controller/ directory.
  3. Inside our new application/classes/controller/user directory, move the Profile Controller inside of it, and change its class declaration to match its new location:
    class Controller_User_Profile extends Controller_Application
  4. Inside the new application/classes/controller/user directory, move the Messages Controller inside of it, and change its class declaration to match it’s new location:
    class Controller_User_Messages extends Controller {
  5. A quick reload of http://localhost/egotist/profile/2/someuser shows everything is working fine, as nothing has changed.

What just happened?

Inside our route named ‘profile’, we added an additional array key for directory in the defaults array settings, and assigned it the value of ‘user’. This tells Kohana that the controllers for this route will need to be located inside a classes/controller/user directory, and will need to start with the class name Controller_User, based on the framework’s naming conventions for controllers.

We also had to create an additional route for our Messages Controller, since it serves users also, it makes sense to move it into this subdirectory also. Now, when the request for messages is made inside the Profile Controller, Kohana will know to find it in the user subdirectory. After moving our Profile and Messages Controllers inside a subdirectory named user, we had to open the files and change their class names to include their path, changing the Controller_ prefix to Controller_User.

Now, whenever a request has a URI with a first segment of profile or messages, Kohana will attempt to match it to one of our custom routes.

Summary

Kohana’s routing is one of the fundamental aspects of the framework, and is very important to understand as you move forward as a Kohana developer. By creating and using some custom routes, you were able to see how real world scenarios can be handled gracefully by Kohana.


Further resources on this subject:


Kohana 3.0 Beginner’s Guide Develop professional web applications with Kohana.
Published: August 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Jason D. Straughan

Jason D. Straughan is currently a software engineer at LiveOak 360, Inc., a web development firm with a focus on social networking and custom web applications. He is also a founding partner in Straughan Photography (SanAntonioWeddingPhotography.com) and provides consultation and training to a select number of clients through his web consulting firm. You can follow his blog, and read more about Kohana and web-related topics at http://www.JDStraughan.com

Books From Packt


CodeIgniter 1.7 Professional Development
CodeIgniter 1.7 Professional Development

Building Websites with ExpressionEngine 2
Building Websites with Expression Engine 2

Yii 1.1 Application Development Cookbook
Yii 1.1 Application Development Cookbook

Catalyst 5.8: the Perl MVC Framework
Catalyst 5.8: the Perl MVC Framework

ASP.NET MVC 2 Cookbook
ASP.NET MVC 2 Cookbook

PHP 5 CMS Framework Development - 2nd Edition
PHP 5 CMS Framework Development - 2nd Edition

SilverStripe 2.4 Module Extension, Themes, and Widgets: Beginner's Guide
SilverStripe 2.4 Module Extension, Themes, and Widgets: Beginner's Guide

Expert PHP 5 Tools
Expert PHP 5 Tools


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
H
K
W
Z
7
V
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software