URL Router

URL Routing in Alloy is simple and straightforward. URL route patterns follow an easy to read syntax and use in-place named parameters, so there is no need to use regular expressions in most cases (although you can if you want to).

Basics

The URL router is like a layer sitting between the URL a user types in and the file that actually handles the request. The URL router works in conjunction with Apache's mod_rewrite or IIS Rewrite (Windows) to catch each request and send it through the front controller to parse the URL based on defined patterns. This approach allows you to create almost any URL structure you want to without having to create additional files or folders in certain places to handle the request. For example, you could have a URL that looks like the sub-directory "/conferences", but you would not have to make the actual subdirectory for it to work. Using the default routes, that would load the "Conferences" module and call the "indexAction" method on the controller. If you specify your own custom routing rule, however, you could make it load an "Events" module with a custom "type" request flag set to "conferences", or almost anything else you wanted to do. This de-couples the physical URLs from the actual code and file paths, making them extremely flexible -- far more so than simply splitting the URL by path separator, for instance.

URL route lookups are one of the first tasks Alloy performs for each request. The defined URL routes determine how the requested URL will be handled by your application, depending on which defined route is matched for that specific request. URL routes are matched in the order they are defined, on a first-match basis. This means that once a route is matched, the dispatch process begins, and no more routes are attempted for that request. In general, static routes should be defined first, and routes that are more specific should be higher in the order than routes that are more generic.

The only requirements to define a new route is that each route must have at least a name and a URL. The name must be unique, and the URL can either be static (no parameters), or dynamic (with named parameters).

Usage Within Alloy

If you are adding the routes within a project powered by Alloy Framework, the main route definitions are located in app/config/routes.php. If you are adding routes within that file, a $router variable is already available for you to use. If you are adding routes elsewhere in your application or project, you will need an instance of the Alloy kernel to get the current instance of the Router object.


            
// Get Alloy kernel instance $kernel = \Kernel(); // Get router object from kernel $router = $kernel->router();

          

Usage Outside Alloy / In Your Own Project

If you are using Alloy's URL router outside of Alloy framework or in another project as a standalone library, you can simply instantiate the Router directly. The Router consists of two files Alloy\Router and Alloy\Router\Route that you will need to have before you can use it. There are no other file or outside library dependencies.


            
// Ensure both required files are loaded (or use your own Autoloader) require 'path/to/lib/Alloy/Router.php'; require 'path/to/lib/Alloy/Router/Route.php'; // Get Alloy router directly $router = new Alloy\Router();

          

Once you have an instance of the \Alloy\Router object, routes can be added by calling the "route" method on the Router object.

Typical MVC-Style Routes


            
// Controller only $router->route('controller', '/<:controller>')) ->defaults(array( 'action' => 'index' )); // Controller / Action (+ optional item) $router->route('controller_action', '/<:controller>/<:action>(/<:item>)'));

          

Named Parameters

Named parameters are defined in-place within the URL, and come in four different varieties, depending on the type of content you want to capture.


            
// <:param> // Alphanumeric capture (0-9a-zA-Z-_) $router->route('pet_search', '/pets/<:animal>'); // <#param> // Numeric capture (0-9) $router->route('pet_view', '/pets/view/<#id>'); // <*param> // Wildcard capture (.*) - matches anything, including directory separators $router->route('page_edit', '/<*page_url>/edit'); // <:param|[::regex::]> // Custom Regex $router->route('city_events', '/events/<:city_state|[a-z\-a-z{2}]>');

          

Different types of named parameters can be used together to form more useful routes:


            
// View vehicle record $router->route('vehicle', '/<#year>/<:make>/<:model>') ->defaults(array( 'module' => 'Vehicle', 'action' => 'view', 'format' => 'html' ));

          
For a route to be usable within Alloy, it must have two parameters defined - module and action. Alloy uses these parameters to know which module to load, and which action to call within the dispatcher. Note that these do not have to be named parameters within the route URL itself.

Static Routes

Static routes are great for arbitrary URL paths that need to be mapped to specific Controllers or actions, even though they may not follow any pattern or convention.


            
// Default "home" route $router->route('default', '/') ->defaults(array( 'module' => 'Home', 'action' => 'index', 'format' => 'html' )); // User login/logout $router->route('login', '/login') ->defaults(array( 'module' => 'User', 'action' => 'login' )); $router->route('logout', '/logout') ->defaults(array( 'module' => 'User', 'action' => 'logout' )); // Static pages $router->route('page_about', '/about') ->defaults(array( 'module' => 'Page', 'action' => 'view', 'page' => 'about' ));

          

Route Matching / Parsing

Routes are parsed with the method "match" on \Alloy\Router. The first parameter is the HTTP method (GET, POST, DELETE, etc.), and the second parameter is the URL string. When the route is parsed, an array of matched key/value parameters will be returned. The results of the vehicle route above would look like this:


            
// URL like this $url = '2008/ferrari/f430'; // Match URL to provided routes $params = $router->match('GET', $url); /* // Resulting params would look like this: $params = array( 'year' => '2008', 'make' => 'ferrari', 'model' => 'f430', 'module' => 'Vehicle', 'action' => 'view', 'format' => 'html' ); */

          

The results of the match are set on the request object for easy access within your application, and the module and action keys are passed to the dispatcher to execute the appropriate module controller method. If either the module or action keys are not present after the router match, Alloy will throw an error and will not be able to continue execution. If the Router match method returns false, no route could be matched and Alloy will return a 404 HTTP response.

Reverse URL Matching

The Alloy URL Router has the ability to take an array of named parameters and perform a reverse match against the defined routes to produce a string URL (substituting the named array keys with their corresponding named URL segments). This type of URL generation is recommended for all URLs in your application, because it makes them able to handle URL structure changes without any resulting code changes. No more digging through all your views and templates when your boss decides they want the city name in front of event URLs instead of after them.

Using Alloy Kernel's 'url' Helper

Within Alloy framework controller or view scripts, you will usually want to build URLs with the Kernel 'url' method, because it gives you more options and is easier to use. It provides extra functionality on top of the base URL Router that is specific to Alloy framework, like generating URLs with or without the URL rewrite config setting enabled, auto-prepending the base URL, and extra handling for query strings and other arbitrary parameters.


            
// Inside a view template or controller // Result: http://yoursite.com/2008/ferrari/f430 echo $view->url(array( 'year' => '2008', 'make' => 'ferrari', 'model' => 'f430' ), 'vehicle');

          

Using the Router 'url' method directly

If you are using the Alloy URL router outside of the framework or as a standalone library, you will be using the 'url' method on an instance of the \Alloy\Router object. This method takes exactly two parameters for routes with parameters - an array and route name, and one parameter for static routes - the route name.


            
// Array of named params + route name // Result: /2008/ferrari/f430 echo $router->url(array( 'year' => '2008', 'make' => 'ferrari', 'model' => 'f430' ), 'vehicle'); // Only route name for static routes // Result: /about echo $router->url('page_about');

          

RESTful URL Routes

RESTful routes are taken into consideration by default. It is trivially easy to change route behavior depending on which HTTP method is used to access the metched URL route. The 'route' method returns a new \Alloy\Router\Route object for each defined route, so methods can be chained off of each route to modify its behavior.


            
// RESTful resource route $router->route('module_item', '/<:module>/<#item>') ->defaults(array('action' => 'view')) ->get(array('action' => 'view')) ->post(array('action' => 'post')) ->put(array('action' => 'put')) ->delete(array('action' => 'delete'));

          

As you may be able to tell with the given code, this single URL can return a number of different actions to run, depending on the HTTP method used in the request.

  • GET /events/721 == ('action' => 'view')
  • POST /events/721 == ('action' => 'post')
  • PUT /events/721 == ('action' => 'put')
  • DELETE /events/721 == ('action' => 'delete')

Within Alloy, the dispatcher will translate these actions to viewAction, postMethod, putMethod, and deleteMethod, respectively. This is so the request context of the actions can be isolated from typical GET requests (i.e. a GET request will never be able to DELETE an item, or even be able to reach the corresponding controller method). Read more about how this works within Alloy in the Controllers manual page.

Alloy Default Routes

Defined routes that ship with Alloy. Located at: app/config/routes.php


            
/** * Default routes */ $router->route('module_item_action', '/<:module>/<#item>/<:action>(.<:format>)') ->defaults(array('format' => 'html')); $router->route('module_item', '/<:module>/<#item>(.<:format>)') ->defaults(array('action' => 'view', 'format' => 'html')) ->get(array('action' => 'view')) ->post(array('action' => 'post')) ->put(array('action' => 'put')) ->delete(array('action' => 'delete')); $router->route('module_action', '/<:module>/<:action>(.<:format>)') ->defaults(array('format' => 'html')); $router->route('module', '/<:module>(.<:format>)') ->defaults(array('action' => 'index', 'format' => 'html')); $router->route('default', '/') ->defaults(array('module' => 'Home', 'action' => 'index', 'format' => 'html'));

          

Related Links