URI Routing

Typically there is a one-to-one relationship between a URL string and its correspondingcontroller class/method. The segments in a URI normally follow this pattern:

  1. example.com/class/function/id/

In some instances, however, you may want to remap this relationship so that a differentclass/method can be called instead of the one corresponding to the URL.

For example, let’s say you want your URLs to have this prototype:

  1. example.com/product/1/
  2. example.com/product/2/
  3. example.com/product/3/
  4. example.com/product/4/

Normally the second segment of the URL is reserved for the method name, but in the exampleabove it instead has a product ID. To overcome this, CodeIgniter allows you to remap the URI handler.

Setting your own routing rules

Routing rules are defined in the app/config/Routes.php file. In it you’ll see thatit creates an instance of the RouteCollection class that permits you to specify your own routing criteria.Routes can be specified using placeholders or Regular Expressions.

A route simply takes the URI on the left, and maps it to the controller and method on the right,along with any parameters that should be passed to the controller. The controller and method shouldbe listed in the same way that you would use a static method, by separating the fully-namespaced classand its method with a double-colon, like Users::list. If that method requires parameters to bepassed to it, then they would be listed after the method name, separated by forward-slashes:

  1. // Calls the $Users->list()
  2. Users::list
  3. // Calls $Users->list(1, 23)
  4. Users::list/1/23

Placeholders

A typical route might look something like this:

  1. $routes->add('product/(:num)', 'App\Catalog::productLookup');

In a route, the first parameter contains the URI to be matched, while the second parametercontains the destination it should be re-routed to. In the above example, if the literal word“product” is found in the first segment of the URL, and a number is found in the second segment,the “AppCatalog” class and the “productLookup” method are used instead.

Placeholders are simply strings that represent a Regular Expression pattern. During the routingprocess, these placeholders are replaced with the value of the Regular Expression. They are primarilyused for readability.

The following placeholders are available for you to use in your routes:

  • (:any) will match all characters from that point to the end of the URI. This may include multiple URI segments.
  • (:segment) will match any character except for a forward slash (/) restricting the result to a single segment.
  • (:num) will match any integer.
  • (:alpha) will match any string of alphabetic characters
  • (:alphanum) will match any string of alphabetic characters or integers, or any combination of the two.
  • (:hash) is the same as :segment, but can be used to easily see which routes use hashed ids (see the Model docs).

Note

{locale} cannot be used as a placeholder or other part of the route, as it is reserved for usein localization.

Examples

Here are a few basic routing examples:

  1. $routes->add('journals', 'App\Blogs');

A URL containing the word “journals” in the first segment will be remapped to the “AppBlogs” class,and the default method, which is usually index():

  1. $routes->add('blog/joe', 'Blogs::users/34');

A URL containing the segments “blog/joe” will be remapped to the “Blogs” class and the “users” method.The ID will be set to “34”:

  1. $routes->add('product/(:any)', 'Catalog::productLookup');

A URL with “product” as the first segment, and anything in the second will be remapped to the “Catalog” classand the “productLookup” method:

  1. $routes->add('product/(:num)', 'Catalog::productLookupByID/$1';

A URL with “product” as the first segment, and a number in the second will be remapped to the “Catalog” classand the “productLookupByID” method passing in the match as a variable to the method.

Important

While the add() method is convenient, it is recommended to always use the HTTP-verb-basedroutes, described below, as it is more secure. It will also provide a slight performance increase, sinceonly routes that match the current request method are stored, resulting in fewer routes to scan throughwhen trying to find a match.

Custom Placeholders

You can create your own placeholders that can be used in your routes file to fully customize the experienceand readability.

You add new placeholders with the addPlaceholder method. The first parameter is the string to be used asthe placeholder. The second parameter is the Regular Expression pattern it should be replaced with.This must be called before you add the route:

  1. $routes->addPlaceholder('uuid', '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}');
  2. $routes->add('users/(:uuid)', 'Users::show/$1');

Regular Expressions

If you prefer you can use regular expressions to define your routing rules. Any valid regular expressionis allowed, as are back-references.

Important

Note: If you use back-references you must use the dollar syntax rather than the double backslash syntax.A typical RegEx route might look something like this:

  1. $routes->add('products/([a-z]+)/(\d+)', 'Products::show/$1/id_$2');

In the above example, a URI similar to products/shirts/123 would instead call the show methodof the Products controller class, with the original first and second segment passed as arguments to it.

With regular expressions, you can also catch a segment containing a forward slash (‘/’), which would usuallyrepresent the delimiter between multiple segments.

For example, if a user accesses a password protected area of your web application and you wish to be able toredirect them back to the same page after they log in, you may find this example useful:

  1. $routes->add('login/(.+)', 'Auth::login/$1');

For those of you who don’t know regular expressions and want to learn more about them,regular-expressions.info might be a good starting point.

Important

Note: You can also mix and match wildcards with regular expressions.

Closures

You can use an anonymous function, or Closure, as the destination that a route maps to. This function will beexecuted when the user visits that URI. This is handy for quickly executing small tasks, or even just showinga simple view:

  1. $routes->add('feed', function()
  2. {
  3. $rss = new RSSFeeder();
  4. return $rss->feed('general');
  5. });

Mapping multiple routes

While the add() method is simple to use, it is often handier to work with multiple routes at once, usingthe map() method. Instead of calling the add() method for each route that you need to add, you candefine an array of routes and then pass it as the first parameter to the map() method:

  1. $routes = [];
  2. $routes['product/(:num)'] = 'Catalog::productLookupById';
  3. $routes['product/(:alphanum)'] = 'Catalog::productLookupByName';
  4.  
  5. $collection->map($routes);

Redirecting Routes

Any site that lives long enough is bound to have pages that move. You can specify routes that should redirectto other routes with the addRedirect() method. The first parameter is the URI pattern for the old route. Thesecond parameter is either the new URI to redirect to, or the name of a named route. The third parameter isthe HTTP status code that should be sent along with the redirect. The default value is 302 which is a temporaryredirect and is recommended in most cases:

  1. $routes->add('users/profile', 'Users::profile', ['as' => 'profile']);
  2.  
  3. // Redirect to a named route
  4. $routes->addRedirect('users/about', 'profile');
  5. // Redirect to a URI
  6. $routes->addRedirect('users/about', 'users/profile');

If a redirect route is matched during a page load, the user will be immediately redirected to the new page before acontroller can be loaded.

Grouping Routes

You can group your routes under a common name with the group() method. The group name becomes a segment thatappears prior to the routes defined inside of the group. This allows you to reduce the typing needed to build out anextensive set of routes that all share the opening string, like when building an admin area:

  1. $routes->group('admin', function($routes)
  2. {
  3. $routes->add('users', 'Admin\Users::index');
  4. $routes->add('blog', 'Admin\Blog::index');
  5. });

This would prefix the ‘users’ and ‘blog” URIs with “admin”, handling URLs like /admin/users and /admin/blog.It is possible to nest groups within groups for finer organization if you need it:

  1. $routes->group('admin', function($routes)
  2. {
  3. $routes->group('users', function($routes)
  4. {
  5. $routes->add('list', 'Admin\Users::list');
  6. });
  7.  
  8. });

This would handle the URL at admin/users/list.

If you need to assign options to a group, like a namespace, do it before the callback:

  1. $routes->group('api', ['namespace' => 'App\API\v1'], function($routes)
  2. {
  3. $routes->resource('users');
  4. });

This would handle a resource route to the App\API\v1\Users controller with the /api/users URI.

You can also use a specific filter for a group of routes. This will alwaysrun the filter before or after the controller. This is especially handy during authentication or api logging:

  1. $routes->group('api', ['filter' => 'api-auth'], function($routes)
  2. {
  3. $routes->resource('users');
  4. });

The value for the filter must match one of the aliases defined within app/Config/Filters.php.

Environment Restrictions

You can create a set of routes that will only be viewable in a certain environment. This allows you to createtools that only the developer can use on their local machines that are not reachable on testing or production servers.This can be done with the environment() method. The first parameter is the name of the environment. Anyroutes defined within this closure are only accessible from the given environment:

  1. $routes->environment('development', function($routes)
  2. {
  3. $routes->add('builder', 'Tools\Builder::index');
  4. });

Reverse Routing

Reverse routing allows you to define the controller and method, as well as any parameters, that a link should goto, and have the router lookup the current route to it. This allows route definitions to change without you havingto update your application code. This is typically used within views to create links.

For example, if you have a route to a photo gallery that you want to link to, you can use the route_to() helperfunction to get the current route that should be used. The first parameter is the fully qualified Controller and method,separated by a double colon (::), much like you would use when writing the initial route itself. Any parameters thatshould be passed to the route are passed in next:

  1. // The route is defined as:
  2. $routes->add('users/(:id)/gallery(:any)', 'App\Controllers\Galleries::showUserGallery/$1/$2');
  3.  
  4. // Generate the relative URL to link to user ID 15, gallery 12
  5. // Generates: /users/15/gallery/12
  6. <a href="<?= route_to('App\Controllers\Galleries::showUserGallery', 15, 12) ?>">View Gallery</a>

Using Named Routes

You can name routes to make your application less fragile. This applies a name to a route that can be calledlater, and even if the route definition changes, all of the links in your application built with route_towill still work without you having to make any changes. A route is named by passing in the as optionwith the name of the route:

  1. // The route is defined as:
  2. $routes->add('users/(:id)/gallery(:any)', 'Galleries::showUserGallery/$1/$2', ['as' => 'user_gallery');
  3.  
  4. // Generate the relative URL to link to user ID 15, gallery 12
  5. // Generates: /users/15/gallery/12
  6. <a href="<?= route_to('user_gallery', 15, 12) ?>">View Gallery</a>

This has the added benefit of making the views more readable, too.

Using HTTP verbs in routes

It is possible to use HTTP verbs (request method) to define your routing rules. This is particularlyuseful when building RESTFUL applications. You can use any standard HTTP verb (GET, POST, PUT, DELETE, etc).Each verb has its own method you can use:

  1. $routes->get('products', 'Product::feature');
  2. $routes->post('products', 'Product::feature');
  3. $routes->put('products/(:num)', 'Product::feature');
  4. $routes->delete('products/(:num)', 'Product::feature');

You can supply multiple verbs that a route should match by passing them in as an array to the match method:

  1. $routes->match(['get', 'put'], 'products', 'Product::feature');

Command-Line only Routes

You can create routes that work only from the command-line, and are inaccessible from the web browser, with thecli() method. This is great for building cronjobs or CLI-only tools. Any route created by any of the HTTP-verb-basedroute methods will also be inaccessible from the CLI, but routes created by the any() method will still beavailable from the command line:

  1. $routes->cli('migrate', 'App\Database::migrate');

Global Options

All of the methods for creating a route (add, get, post, resource etc) can take an array of options thatcan modify the generated routes, or further restrict them. The $options array is always the last parameter:

  1. $routes->add('from', 'to', $options);
  2. $routes->get('from', 'to', $options);
  3. $routes->post('from', 'to', $options);
  4. $routes->put('from', 'to', $options);
  5. $routes->head('from', 'to', $options);
  6. $routes->options('from', 'to', $options);
  7. $routes->delete('from', 'to', $options);
  8. $routes->patch('from', 'to', $options);
  9. $routes->match(['get', 'put'], 'from', 'to', $options);
  10. $routes->resource('photos', $options);
  11. $routes->map($array, $options);
  12. $routes->group('name', $options, function());

Applying Filters

You can alter the behavior of specific routes by supplying a filter to run before or after the controller. This is especially handy during authentication or api logging:

  1. $routes->add('admin',' AdminController::index', ['filter' => 'admin-auth']);

The value for the filter must match one of the aliases defined within app/Config/Filters.php. You may also supply parameters to be passed to the filter’s before() and after() methods:

  1. $routes->add('users/delete/(:segment)', 'AdminController::index', ['filter' => 'admin-auth:dual,noreturn']);

See Controller filters for more information on setting up filters.

Assigning Namespace

While a default namespace will be prepended to the generated controllers (see below), you can also specifya different namespace to be used in any options array, with the namespace option. The value should be thenamespace you want modified:

  1. // Routes to \Admin\Users::index()
  2. $routes->add('admin/users', 'Users::index', ['namespace' => 'Admin']);

The new namespace is only applied during that call for any methods that create a single route, like get, post, etc.For any methods that create multiple routes, the new namespace is attached to all routes generated by that functionor, in the case of group(), all routes generated while in the closure.

Limit to Hostname

You can restrict groups of routes to function only in certain domain or sub-domains of your applicationby passing the “hostname” option along with the desired domain to allow it on as part of the options array:

  1. $collection->get('from', 'to', ['hostname' => 'accounts.example.com']);

This example would only allow the specified hosts to work if the domain exactly matched “accounts.example.com”.It would not work under the main site at “example.com”.

Limit to Subdomains

When the subdomain option is present, the system will restrict the routes to only be available on thatsub-domain. The route will only be matched if the subdomain is the one the application is being viewed through:

  1. // Limit to media.example.com
  2. $routes->add('from', 'to', ['subdomain' => 'media']);

You can restrict it to any subdomain by setting the value to an asterisk, (*). If you are viewing from a URLthat does not have any subdomain present, this will not be matched:

  1. // Limit to any sub-domain
  2. $routes->add('from', 'to', ['subdomain' => '*']);

Important

The system is not perfect and should be tested for your specific domain before being used in production.Most domains should work fine but some edge case ones, especially with a period in the domain itself (not usedto separate suffixes or www) can potentially lead to false positives.

Offsetting the Matched Parameters

You can offset the matched parameters in your route by any numeric value with the offset option, with thevalue being the number of segments to offset.

This can be beneficial when developing API’s with the first URI segment being the version number. It can alsobe used when the first parameter is a language string:

  1. $routes->get('users/(:num)', 'users/show/$1', ['offset' => 1]);
  2.  
  3. // Creates:
  4. $routes['users/(:num)'] = 'users/show/$2';

Routes Configuration Options

The RoutesCollection class provides several options that affect all routes, and can be modified to meet yourapplication’s needs. These options are available at the top of /app/Config/Routes.php.

Default Namespace

When matching a controller to a route, the router will add the default namespace value to the front of the controllerspecified by the route. By default, this value is empty, which leaves each route to specify the fully namespacedcontroller:

  1. $routes->setDefaultNamespace('');
  2.  
  3. // Controller is \Users
  4. $routes->add('users', 'Users::index');
  5.  
  6. // Controller is \Admin\Users
  7. $routes->add('users', 'Admin\Users::index');

If your controllers are not explicitly namespaced, there is no need to change this. If you namespace your controllers,then you can change this value to save typing:

  1. $routes->setDefaultNamespace('App');
  2.  
  3. // Controller is \App\Users
  4. $routes->add('users', 'Users::index');
  5.  
  6. // Controller is \App\Admin\Users
  7. $routes->add('users', 'Admin\Users::index');

Default Controller

When a user visits the root of your site (i.e. example.com) the controller to use is determined by the value set bythe setDefaultController() method, unless a route exists for it explicitly. The default value for this is Homewhich matches the controller at /app/Controllers/Home.php:

  1. // example.com routes to app/Controllers/Welcome.php
  2. $routes->setDefaultController('Welcome');

The default controller is also used when no matching route has been found, and the URI would point to a directoryin the controllers directory. For example, if the user visits example.com/admin, if a controller was found at/app/Controllers/admin/Home.php it would be used.

Default Method

This works similar to the default controller setting, but is used to determine the default method that is usedwhen a controller is found that matches the URI, but no segment exists for the method. The default value isindex:

  1. $routes->setDefaultMethod('listAll');

In this example, if the user were to visit example.com/products, and a Products controller existed, theProducts::listAll() method would be executed.

Translate URI Dashes

This option enables you to automatically replace dashes (‘-‘) with underscores in the controller and methodURI segments, thus saving you additional route entries if you need to do that. This is required because thedash isn’t a valid class or method name character and would cause a fatal error if you try to use it:

  1. $routes->setTranslateURIDashes(true);

Use Defined Routes Only

When no defined route is found that matches the URI, the system will attempt to match that URI against thecontrollers and methods as described above. You can disable this automatic matching, and restrict routesto only those defined by you, by setting the setAutoRoute() option to false:

  1. $routes->setAutoRoute(false);

404 Override

When a page is not found that matches the current URI, the system will show a generic 404 view. You can changewhat happens by specifying an action to happen with the set404Override() option. The value can be eithera valid class/method pair, just like you would show in any route, or a Closure:

  1. // Would execute the show404 method of the App\Errors class
  2. $routes->set404Override('App\Errors::show404');
  3.  
  4. // Will display a custom view
  5. $routes->set404Override(function()
  6. {
  7. echo view('my_errors/not_found.html');
  8. });