Routing is the process of mapping URL's to Controller and actions. In the introduction we defined a new custom route for our TeamController mapping any teams URL to our TeamController

If you're using the cms module with and dealing with Page objects then for your custom Page Type controllers you would extend ContentController or PageController. You don't need to define the routes value as the cms handles routing.

These routes by standard, go into a routes.yml file in your applications _config folder alongside your other Configuration information.


Name: mysiteroutes
  - '#rootroutes'
  - '#coreroutes'
    'teams//$Action/$ID/$Name': 'TeamController'
    'player/': 'PlayerController'
    '': 'HomeController'

To understand the syntax for the routes.yml file better, read the Configuration documentation.


'teams//$Action/$ID/$Name': 'TeamController'

This route has defined that any URL beginning with team should create, and be handled by a TeamController instance.

It also contains 3 parameters or params for short. $Action, $ID and $Name. These variables are placeholders which will be filled when the user makes their request. Request parameters are available on the HTTPRequest object and able to be pulled out from a controller using $this->getRequest()->param($name).

All Controllers have access to $this->getRequest() for the request object and $this->getResponse() for the response.

Here is what those parameters would look like for certain requests

// GET /teams/


// Array
// (
//   [Action] => null
//   [ID] => null
//   [Name] => null
// )

// GET /teams/players/


// Array
// (
//   [Action] => 'players'
//   [ID] => null
//   [Name] => null
// )

// GET /teams/players/1


// Array
// (
//   [Action] => 'players'
//   [ID] => 1
//   [Name] => null
// )

You can also fetch one parameter at a time.

// GET /teams/players/1/

echo $this->getRequest()->param('ID');
// returns '1'

URL Patterns

The RequestHandler class will parse all rules you specify against the following patterns. The most specific rule will be the one followed for the response.

A rule must always start with alphabetical ([A-Za-z]) characters or a $Variable declaration
Pattern Description
$ Param Variable - Starts the name of a paramater variable, it is optional to match this unless ! is used
! Require Variable - Placing this after a parameter variable requires data to be present for the rule to match
// Shift Point - Declares that only variables denoted with a $ are parsed into the $params AFTER this point in the regex
'teams/$Action/$ID/$OtherID': 'TeamController' 

# /teams/ {#teams}
# /teams/players/ {#teams-players}
# /teams/ {#teams-2}

Standard URL handler syntax. For any URL that contains 'team' this rule will match and hand over execution to the matching controller. The TeamsController is passed an optional action, id and other id parameters to do any more decision making.

'teams/$Action!/$ID!/': 'TeamController'

This does the same matching as the previous example, any URL starting with teams will look at this rule but both $Action and $ID are required. Any requests to team/ will result in a 404 error rather than being handed off to the TeamController.

'admin/help//$Action/$ID: 'AdminHelp'

Match an url starting with /admin/help/, but don't include /help/ as part of the action (the shift point is set to start parsing variables and the appropriate controller action AFTER the //).

URL Handlers

You must use the $url_handlers static array described here if your URL pattern does not use the Controller class's default pattern of $Action//$ID/$OtherID. If you fail to do so, and your pattern has more than 2 parameters, your controller will throw the error "I can't handle sub-URLs of a class name object" with HTTP status 404.

In the above example the URLs were configured using the Director rules in the routes.yml file. Alternatively you can specify these in your Controller class via the $url_handlers static array. This array is processed by the RequestHandler at runtime once the Controller has been matched.

This is useful when you want to provide custom actions for the mapping of teams/*. Say for instance we want to respond coaches, and staff to the one controller action payroll.


use SilverStripe\Control\Controller;

class TeamController extends Controller
    private static $allowed_actions = [

    private static $url_handlers = [
        'staff/$ID/$Name' => 'payroll',
        'coach/$ID/$Name' => 'payroll'

The syntax for the $url_handlers array users the same pattern matches as the YAML configuration rules.

Now let’s consider a more complex example from a real project, where using $url_handlers is mandatory. In this example, the URLs are of the form, followed by 5 parameters. The PHP controller class specifies the URL pattern in $url_handlers. Notice that it defines 5 parameters.

use SilverStripe\CMS\Controllers\ContentController;

class FeedController extends ContentController
    private static $allowed_actions = ['go'];
    private static $url_handlers = [
        'go/$UserName/$AuthToken/$Timestamp/$OutputType/$DeleteMode' => 'go'

    public function go()
        /* more processing goes here */

The YAML rule, in contrast, is simple. It needs to provide only enough
information for the framework to choose the desired controller.

Director: rules:

'feed': 'FeedController'

## Related Lessons {#related-lessons}
* [Creating filtered views](
* [Controller actions / DataObjects as pages](
## Links {#links}

* <a href="\Control\Controller&version=4&module=framework">Controller</a> API documentation
* <a href="\Control\Director&version=4&module=framework">Director</a> API documentation
* [Example routes: framework](
* [Example routes: cms](

Was this article helpful?